Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/v1.6' into dev

# Conflicts:
#	src/api/generated/index.ts
#	src/pages/tourOrder/formTourOrder.vue
#	src/router/systemRouters.ts
limeng 2 mēneši atpakaļ
vecāks
revīzija
edffa4a3ac
49 mainītis faili ar 10022 papildinājumiem un 5 dzēšanām
  1. 27 0
      src/api/generated/index.ts
  2. 43 0
      src/api/generated/tourBrowseRecordsController.ts
  3. 43 0
      src/api/generated/tourFansController.ts
  4. 43 0
      src/api/generated/tourImComplaintTypeController.ts
  5. 43 0
      src/api/generated/tourImComplaitController.ts
  6. 49 0
      src/api/generated/tourImGroupController.ts
  7. 43 0
      src/api/generated/tourImGroupInvitationController.ts
  8. 43 0
      src/api/generated/tourImGroupTypeController.ts
  9. 43 0
      src/api/generated/tourImMemberController.ts
  10. 43 0
      src/api/generated/tourImMessageController.ts
  11. 43 0
      src/api/generated/tourImSensitiveWordAllowController.ts
  12. 43 0
      src/api/generated/tourImSensitiveWordDenyController.ts
  13. 44 0
      src/api/system/DictionaryController.ts
  14. 1 1
      src/components/Dialog/types.d.ts
  15. 300 0
      src/pages/ImComplaint/formEditTourImComplaintType.vue
  16. 493 0
      src/pages/ImComplaint/formEditTourImComplait.vue
  17. 409 0
      src/pages/ImComplaint/formTourImComplaintType.vue
  18. 506 0
      src/pages/ImComplaint/formTourImComplait.vue
  19. 607 0
      src/pages/ImGroup/formEditTourImGroup.vue
  20. 278 0
      src/pages/ImGroup/formEditTourImGroupInvitation.vue
  21. 376 0
      src/pages/ImGroup/formEditTourImGroupType.vue
  22. 260 0
      src/pages/ImGroup/formEditTourImMember.vue
  23. 524 0
      src/pages/ImGroup/formEditTourImMessage.vue
  24. 789 0
      src/pages/ImGroup/formTourImGroup.vue
  25. 528 0
      src/pages/ImGroup/formTourImGroupInvitation.vue
  26. 558 0
      src/pages/ImGroup/formTourImGroupType.vue
  27. 595 0
      src/pages/ImGroup/formTourImMember.vue
  28. 559 0
      src/pages/ImGroup/formTourImMessage.vue
  29. 225 0
      src/pages/SensitiveWord/formEditTourImSensitiveWordAllow.vue
  30. 225 0
      src/pages/SensitiveWord/formEditTourImSensitiveWordDeny.vue
  31. 365 0
      src/pages/SensitiveWord/formTourImSensitiveWordAllow.vue
  32. 365 0
      src/pages/SensitiveWord/formTourImSensitiveWordDeny.vue
  33. 363 0
      src/pages/TourBrowseRecords/formTourBrowseRecords.vue
  34. 319 0
      src/pages/tourFans/formEditTourFans.vue
  35. 437 0
      src/pages/tourFans/formTourFans.vue
  36. 2 2
      src/pages/tourHouseRentInfo/formTourHouseRentInfo.vue
  37. 2 2
      src/pages/tourOrder/formTourOrder.vue
  38. 77 0
      src/router/systemRouters.ts
  39. 27 0
      src/types/table/tourBrowseRecords.d.ts
  40. 23 0
      src/types/table/tourFans.d.ts
  41. 25 0
      src/types/table/tourImComplaintType.d.ts
  42. 30 0
      src/types/table/tourImComplait.d.ts
  43. 44 0
      src/types/table/tourImGroup.d.ts
  44. 30 0
      src/types/table/tourImGroupInvitation.d.ts
  45. 28 0
      src/types/table/tourImGroupType.d.ts
  46. 42 0
      src/types/table/tourImMember.d.ts
  47. 40 0
      src/types/table/tourImMessage.d.ts
  48. 10 0
      src/types/table/tourImSensitiveWordAllow.d.ts
  49. 10 0
      src/types/table/tourImSensitiveWordDeny.d.ts

+ 27 - 0
src/api/generated/index.ts

@@ -53,6 +53,21 @@ import TourCarContractMenuController from './tourCarContractMenuController';
 import TourCarCategoryController from './tourCarCategoryController';
 import TourBusinessBannerController from './tourBusinessBannerController';
 import VisaMetarialsListController from "./visaMetarialsListController";
+import TourFansController from './tourFansController';
+import TourBrowseRecordsController from './tourBrowseRecordsController';
+import TourImComplaintTypeController from './tourImComplaintTypeController';
+import TourImComplaitController from './tourImComplaitController';
+import TourImSensitiveWordAllowController from './tourImSensitiveWordAllowController';
+import TourImSensitiveWordDenyController from './tourImSensitiveWordDenyController';
+import TourImGroupTypeController from './tourImGroupTypeController';
+import TourImGroupInvitationController from './tourImGroupInvitationController';
+import TourImMemberController from './tourImMemberController';
+import TourImMessageController from './tourImMessageController';
+import TourImGroupController from './tourImGroupController';
+
+
+
+import VisaMetarialsListController from "./visaMetarialsListController";
 import TourProjectGroupPurchaseController from './tourProjectGroupPurchaseController';
 import TourProjectGroupPurchaseDetailController from './tourProjectGroupPurchaseDetailController';
 import TourProjectGroupPurchaseApplyController from './tourProjectGroupPurchaseApplyController';
@@ -114,6 +129,18 @@ export {
   TourCarCategoryController,
   TourBusinessBannerController,
   VisaMetarialsListController,
+  TourFansController,
+  TourBrowseRecordsController,
+  TourImComplaintTypeController,
+  TourImComplaitController,
+  TourImSensitiveWordAllowController,
+  TourImSensitiveWordDenyController,
+  TourImGroupTypeController,
+  TourImGroupInvitationController,
+  TourImMemberController,
+  TourImMessageController,
+  TourImGroupController,
+  VisaMetarialsListController,
   TourProjectGroupPurchaseController,
   TourProjectGroupPurchaseDetailController,
   TourProjectGroupPurchaseApplyController,

+ 43 - 0
src/api/generated/tourBrowseRecordsController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourBrowseRecords from '@/types/table/tourBrowseRecords';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourBrowseRecordsData extends TourBrowseRecords {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourBrowseRecordsController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourBrowseRecordsData>>(API_CONTEXT + '/app/tourBrowseRecords/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourBrowseRecordsData>(API_CONTEXT + '/app/tourBrowseRecords/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourBrowseRecords/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourBrowseRecords/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourBrowseRecords/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourBrowseRecords/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourBrowseRecords/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourBrowseRecords/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourBrowseRecords/deleteBatch', params, httpOptions);
+  }
+}

+ 43 - 0
src/api/generated/tourFansController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourFans from '@/types/table/tourFans';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourFansData extends TourFans {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourFansController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourFansData>>(API_CONTEXT + '/app/tourFans/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourFansData>(API_CONTEXT + '/app/tourFans/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourFans/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourFans/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourFans/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourFans/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourFans/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourFans/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourFans/deleteBatch', params, httpOptions);
+  }
+}

+ 43 - 0
src/api/generated/tourImComplaintTypeController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourImComplaintType from '@/types/table/tourImComplaintType';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourImComplaintTypeData extends TourImComplaintType {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourImComplaintTypeController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourImComplaintTypeData>>(API_CONTEXT + '/app/tourImComplaintType/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourImComplaintTypeData>(API_CONTEXT + '/app/tourImComplaintType/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourImComplaintType/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourImComplaintType/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourImComplaintType/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImComplaintType/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImComplaintType/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImComplaintType/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImComplaintType/deleteBatch', params, httpOptions);
+  }
+}

+ 43 - 0
src/api/generated/tourImComplaitController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourImComplait from '@/types/table/tourImComplait';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourImComplaitData extends TourImComplait {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourImComplaitController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourImComplaitData>>(API_CONTEXT + '/app/tourImComplait/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourImComplaitData>(API_CONTEXT + '/app/tourImComplait/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourImComplait/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourImComplait/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourImComplait/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImComplait/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImComplait/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImComplait/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImComplait/deleteBatch', params, httpOptions);
+  }
+}

+ 49 - 0
src/api/generated/tourImGroupController.ts

@@ -0,0 +1,49 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourImGroup from '@/types/table/tourImGroup';
+import { TourImGroupInvitationData } from '@/api/generated/tourImGroupInvitationController';
+import { TourImMemberData } from '@/api/generated/tourImMemberController';
+import { TourImMessageData } from '@/api/generated/tourImMessageController';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourImGroupData extends TourImGroup {
+  tourImGroupInvitationList?: TourImGroupInvitationData[];
+  tourImMemberList?: TourImMemberData[];
+  tourImMessageList?: TourImMessageData[];
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourImGroupController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourImGroupData>>(API_CONTEXT + '/app/tourImGroup/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourImGroupData>(API_CONTEXT + '/app/tourImGroup/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourImGroup/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourImGroup/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourImGroup/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroup/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroup/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroup/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroup/deleteBatch', params, httpOptions);
+  }
+}

+ 43 - 0
src/api/generated/tourImGroupInvitationController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourImGroupInvitation from '@/types/table/tourImGroupInvitation';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourImGroupInvitationData extends TourImGroupInvitation {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourImGroupInvitationController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourImGroupInvitationData>>(API_CONTEXT + '/app/tourImGroupInvitation/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourImGroupInvitationData>(API_CONTEXT + '/app/tourImGroupInvitation/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourImGroupInvitation/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourImGroupInvitation/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourImGroupInvitation/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroupInvitation/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroupInvitation/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroupInvitation/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroupInvitation/deleteBatch', params, httpOptions);
+  }
+}

+ 43 - 0
src/api/generated/tourImGroupTypeController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourImGroupType from '@/types/table/tourImGroupType';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourImGroupTypeData extends TourImGroupType {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourImGroupTypeController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourImGroupTypeData>>(API_CONTEXT + '/app/tourImGroupType/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourImGroupTypeData>(API_CONTEXT + '/app/tourImGroupType/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourImGroupType/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourImGroupType/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourImGroupType/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroupType/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroupType/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroupType/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImGroupType/deleteBatch', params, httpOptions);
+  }
+}

+ 43 - 0
src/api/generated/tourImMemberController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourImMember from '@/types/table/tourImMember';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourImMemberData extends TourImMember {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourImMemberController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourImMemberData>>(API_CONTEXT + '/app/tourImMember/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourImMemberData>(API_CONTEXT + '/app/tourImMember/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourImMember/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourImMember/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourImMember/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImMember/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImMember/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImMember/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImMember/deleteBatch', params, httpOptions);
+  }
+}

+ 43 - 0
src/api/generated/tourImMessageController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourImMessage from '@/types/table/tourImMessage';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourImMessageData extends TourImMessage {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourImMessageController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourImMessageData>>(API_CONTEXT + '/app/tourImMessage/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourImMessageData>(API_CONTEXT + '/app/tourImMessage/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourImMessage/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourImMessage/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourImMessage/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImMessage/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImMessage/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImMessage/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImMessage/deleteBatch', params, httpOptions);
+  }
+}

+ 43 - 0
src/api/generated/tourImSensitiveWordAllowController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourImSensitiveWordAllow from '@/types/table/tourImSensitiveWordAllow';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourImSensitiveWordAllowData extends TourImSensitiveWordAllow {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourImSensitiveWordAllowController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourImSensitiveWordAllowData>>(API_CONTEXT + '/app/tourImSensitiveWordAllow/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourImSensitiveWordAllowData>(API_CONTEXT + '/app/tourImSensitiveWordAllow/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourImSensitiveWordAllow/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourImSensitiveWordAllow/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourImSensitiveWordAllow/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImSensitiveWordAllow/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImSensitiveWordAllow/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImSensitiveWordAllow/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImSensitiveWordAllow/deleteBatch', params, httpOptions);
+  }
+}

+ 43 - 0
src/api/generated/tourImSensitiveWordDenyController.ts

@@ -0,0 +1,43 @@
+import { BaseController } from '@/api/BaseController';
+import { RequestOption } from '@/common/http/types';
+import { ANY_OBJECT } from '@/types/generic';
+import { TableData } from '@/common/types/table';
+import { useUrlBuilder } from '@/common/hooks/useUrl';
+import TourImSensitiveWordDeny from '@/types/table/tourImSensitiveWordDeny';
+import { API_CONTEXT } from '../config';
+
+const { buildGetUrl } = useUrlBuilder();
+
+export interface TourImSensitiveWordDenyData extends TourImSensitiveWordDeny {
+  __cascade_add_temp_id__?: string | number | undefined;
+}
+
+export default class TourImSensitiveWordDenyController extends BaseController {
+  static list(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post<TableData<TourImSensitiveWordDenyData>>(API_CONTEXT + '/app/tourImSensitiveWordDeny/list', params, httpOptions);
+  }
+  static view(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.get<TourImSensitiveWordDenyData>(API_CONTEXT + '/app/tourImSensitiveWordDeny/view', params, httpOptions);
+  }
+  static export(params: ANY_OBJECT, fileName: string) {
+    return super.download(API_CONTEXT + '/app/tourImSensitiveWordDeny/export', params, fileName);
+  }
+  static import(params: ANY_OBJECT) {
+    return super.upload(API_CONTEXT + '/app/tourImSensitiveWordDeny/import', params);
+  }
+  static printUrl(params: ANY_OBJECT) {
+    return buildGetUrl(API_CONTEXT + '/app/tourImSensitiveWordDeny/print', params);
+  }
+  static add(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImSensitiveWordDeny/add', params, httpOptions);
+  }
+  static update(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImSensitiveWordDeny/update', params, httpOptions);
+  }
+  static delete(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImSensitiveWordDeny/delete', params, httpOptions);
+  }
+  static deleteBatch(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return super.post(API_CONTEXT + '/app/tourImSensitiveWordDeny/deleteBatch', params, httpOptions);
+  }
+}

+ 44 - 0
src/api/system/DictionaryController.ts

@@ -387,4 +387,48 @@ export default class DictionaryController extends BaseController {
         });
     });
   }
+  static dictTourImComplaintType(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return new Promise<DictionaryBase>((resolve, reject) => {
+      super.get<DictData[]>('/admin/app/tourImComplaintType/listDict', params, httpOptions)
+        .then(res => {
+          let dictData = new DictionaryBase('投诉类型字典', res.data);
+          resolve(dictData);
+        }).catch(err => {
+          reject(err);
+        });
+    });
+  }
+  static dictTourImGroup(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return new Promise<DictionaryBase>((resolve, reject) => {
+      super.get<DictData[]>('/admin/app/tourImGroup/listDict', params, httpOptions)
+        .then(res => {
+          let dictData = new DictionaryBase('群聊字典表', res.data);
+          resolve(dictData);
+        }).catch(err => {
+          reject(err);
+        });
+    });
+  }
+  static dictTourImGroupType(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return new Promise<DictionaryBase>((resolve, reject) => {
+      super.get<DictData[]>('/admin/app/tourImGroupType/listDict', params, httpOptions)
+        .then(res => {
+          let dictData = new DictionaryBase('聊天群聊类型字典表', res.data);
+          resolve(dictData);
+        }).catch(err => {
+          reject(err);
+        });
+    });
+  }
+  static dictTourImGroupTypeByParentId(params: ANY_OBJECT, httpOptions?: RequestOption) {
+    return new Promise<DictionaryBase>((resolve, reject) => {
+      super.get<DictData[]>('/admin/app/tourImGroupType/listDictByParentId', params, httpOptions)
+        .then(res => {
+          let dictData = new DictionaryBase('聊天群聊类型字典表', res.data);
+          resolve(dictData);
+        }).catch(err => {
+          reject(err);
+        });
+    });
+  }
 }

+ 1 - 1
src/components/Dialog/types.d.ts

@@ -1,5 +1,5 @@
 export interface DialogProp<T> {
   index: string;
   cancel: () => void;
-  submit: (data: T) => void;
+  submit: (data: any) => void;
 }

+ 300 - 0
src/pages/ImComplaint/formEditTourImComplaintType.vue

@@ -0,0 +1,300 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourImComplaintTypeRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="类型名称" prop="TourImComplaintType.typeName">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImComplaintType.typeName"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="类型描述" prop="TourImComplaintType.description">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImComplaintType.description"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="是否启用" prop="TourImComplaintType.enable">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImComplaintType.enable"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="enableWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in enableWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourImComplaintTypeClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourImComplaintType',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImComplaintTypeData } from '@/api/generated/tourImComplaintTypeController';
+import { TourImComplaintTypeController } from '@/api/generated';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourImComplaintTypeRef = ref();
+// 表单数据定义
+type FormEditTourImComplaintTypeData = {
+  TourImComplaintType: TourImComplaintTypeData;
+};
+// 表单数据
+const formData = reactive<FormEditTourImComplaintTypeData>({
+  TourImComplaintType: {
+    // id
+    id: undefined,
+    // 类型名称
+    typeName: undefined,
+    // 类型描述
+    description: undefined,
+    // 是否启用
+    enable: undefined,
+    // 删除标记 1正常 -1删除
+    dataState: undefined,
+    // 创建人id
+    createUserId: undefined,
+    // 创建时间
+    createTime: undefined,
+    // 更新时间
+    updateTime: undefined,
+    // 更新人id
+    updateUserId: undefined,
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourImComplaintType.description': [
+    {required: true, message: '请输入类型描述', trigger: 'blur'}
+  ],
+  'TourImComplaintType.enable': [
+  ],
+  'TourImComplaintType.typeName': [
+    {required: true, message: '请输入类型名称', trigger: 'blur'}
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourImComplaintTypeData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourImComplaintType = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourImComplaintTypeController.view(params).then(res => {
+      formData.TourImComplaintType = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 是否启用下拉数据获取函数
+ */
+const loadEnableDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.Enable.getList(),
+  });
+};
+// 是否启用配置参数
+const enableOptions: DropdownOptions<DictData> = {
+  loadData: loadEnableDropdownList,
+  isTree: false,
+};
+// 是否启用下拉组件
+const enableWidget = useDropdown(enableOptions);
+const { dropdownList: enableWidgetDropdownList } = enableWidget
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourImComplaintType = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourImComplaintType = () => {
+  refreshFormEditTourImComplaintType();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourImComplaintType();
+};
+/**
+ * 保存
+ */
+const onSubmitTourImComplaintTypeClick = () => {
+  formEditTourImComplaintTypeRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourImComplaintType
+      };
+    retFormData.enableDictMap = findItemFromList(enableWidgetDropdownList.value, retFormData.enable, 'id');
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourImComplaintTypeDto: {
+        id: formData.TourImComplaintType.id,
+        description: formData.TourImComplaintType.description,
+        typeName: formData.TourImComplaintType.typeName,
+        enable: formData.TourImComplaintType.enable,
+        createUserId: formData.TourImComplaintType.createUserId,
+        createTime: formData.TourImComplaintType.createTime,
+        updateTime: formData.TourImComplaintType.updateTime,
+        updateUserId: formData.TourImComplaintType.updateUserId,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourImComplaintTypeController.update : TourImComplaintTypeController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourImComplaintTypeData().then(res => {
+    enableWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    if (isEdit.value) refreshFormEditTourImComplaintType();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 493 - 0
src/pages/ImComplaint/formEditTourImComplait.vue

@@ -0,0 +1,493 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourImComplaitRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="用户" prop="TourImComplait.userId">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImComplait.userId"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="userIdWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in userIdWidgetDropdownList"
+                  :key="item.userId"
+                  :label="item.showName"
+                  :value="item.userId"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="群组" prop="TourImComplait.groupId">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImComplait.groupId"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="groupIdWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in groupIdWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="举报类型" prop="TourImComplait.typeId">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImComplait.typeId"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="typeId_copyWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in typeId_copyWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="举报对象" prop="TourImComplait.objectType">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImComplait.objectType"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="objectTypeWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in objectTypeWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="其他违规理由" prop="TourImComplait.elseTypeReason">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImComplait.elseTypeReason"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="举报描述" prop="TourImComplait.description">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImComplait.description"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="举报图片" prop="TourImComplait.image">
+              <custom-upload
+                v-model="imageWidgetFileList"
+                name="uploadFile"
+                :size="layoutStore.defaultFormItemSize"
+                type="expand"
+                :headers="getUploadHeaders"
+                :action="getUploadActionUrl('/admin/app/tourImComplait/upload')"
+                :data="{fieldName: 'image', asImage: true}"
+                :limit="imageWidgetMaxCount"
+                @change="onImageChange"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourImComplaitClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourImComplait',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImComplaitData } from '@/api/generated/tourImComplaitController';
+import {TourImComplaitController, TourUserController} from '@/api/generated';
+import TourUser from "@/types/table/tourUser";
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const {
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourImComplaitRef = ref();
+// 表单数据定义
+type FormEditTourImComplaitData = {
+  TourImComplait: TourImComplaitData;
+};
+// 表单数据
+const formData = reactive<FormEditTourImComplaitData>({
+  TourImComplait: {
+    // id
+    id: undefined,
+    // 举报类型id
+    typeId: undefined,
+    // 类型为其他违规的理由
+    elseTypeReason: undefined,
+    // 举报对象 1用户 2群组
+    objectType: undefined,
+    // 举报描述
+    description: undefined,
+    // 举报图片 最多三张
+    image: undefined,
+    // 删除标记 1正常 -1删除
+    dataState: undefined,
+    // 创建人id
+    createUserId: undefined,
+    // 用户id
+    userId: undefined,
+    // 创建时间
+    createTime: undefined,
+    // 群组id
+    groupId: undefined,
+    // 更新时间
+    updateTime: undefined,
+    // 更新人id
+    updateUserId: undefined,
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourImComplait.groupId': [
+  ],
+  'TourImComplait.elseTypeReason': [
+  ],
+  'TourImComplait.typeId': [
+    {required: true, message: '请输入举报类型', trigger: 'blur'}
+  ],
+  'TourImComplait.objectType': [
+    {required: true, message: '请输入举报对象', trigger: 'blur'}
+  ],
+  'TourImComplait.userId': [
+  ],
+  'TourImComplait.image': [
+  ],
+  'TourImComplait.description': [
+    {required: true, message: '请输入举报描述', trigger: 'blur'}
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourImComplaitData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourImComplait = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourImComplaitController.view(params).then(res => {
+      formData.TourImComplait = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 用户下拉数据获取函数
+ */
+const loadUserIdDropdownList = (): Promise<ListData<TourUser>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    TourUserController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 用户配置参数
+const userIdOptions: DropdownOptions<TourUser> = {
+  loadData: loadUserIdDropdownList,
+  isTree: false,
+};
+// 用户下拉组件
+const userIdWidget = useDropdown(userIdOptions);
+const { dropdownList: userIdWidgetDropdownList } = userIdWidget
+/**
+ * 群组下拉数据获取函数
+ */
+const loadGroupIdDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroup(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 群组配置参数
+const groupIdOptions: DropdownOptions<DictData> = {
+  loadData: loadGroupIdDropdownList,
+  isTree: false,
+};
+// 群组下拉组件
+const groupIdWidget = useDropdown(groupIdOptions);
+const { dropdownList: groupIdWidgetDropdownList } = groupIdWidget
+/**
+ * 举报类型下拉数据获取函数
+ */
+const loadTypeId_copyDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImComplaintType(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 举报类型配置参数
+const typeId_copyOptions: DropdownOptions<DictData> = {
+  loadData: loadTypeId_copyDropdownList,
+  isTree: false,
+};
+// 举报类型下拉组件
+const typeId_copyWidget = useDropdown(typeId_copyOptions);
+const { dropdownList: typeId_copyWidgetDropdownList } = typeId_copyWidget
+/**
+ * 举报对象下拉数据获取函数
+ */
+const loadObjectTypeDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'ComplaintObjectType', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 举报对象配置参数
+const objectTypeOptions: DropdownOptions<DictData> = {
+  loadData: loadObjectTypeDropdownList,
+  isTree: false,
+};
+// 举报对象下拉组件
+const objectTypeWidget = useDropdown(objectTypeOptions);
+const { dropdownList: objectTypeWidgetDropdownList } = objectTypeWidget
+/**
+ * 举报图片上传文件改变
+ */
+const onImageChange = val => {
+  formData.TourImComplait.image = fileListToJson(val);
+};
+// 举报图片上传文件组件
+const imageWidget = useUploadWidget(3);
+const { fileList: imageWidgetFileList, maxCount: imageWidgetMaxCount } = imageWidget;
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourImComplait = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourImComplait = () => {
+  refreshFormEditTourImComplait();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourImComplait();
+};
+/**
+ * 保存
+ */
+const onSubmitTourImComplaitClick = () => {
+  formEditTourImComplaitRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourImComplait
+      };
+    retFormData.userIdDictMap = findItemFromList(userIdWidgetDropdownList.value, retFormData.userId, 'id');
+    retFormData.groupIdDictMap = findItemFromList(groupIdWidgetDropdownList.value, retFormData.groupId, 'id');
+    retFormData.typeIdDictMap = findItemFromList(typeId_copyWidgetDropdownList.value, retFormData.typeId, 'id');
+    retFormData.objectTypeDictMap = findItemFromList(objectTypeWidgetDropdownList.value, retFormData.objectType, 'id');
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourImComplaitDto: {
+        id: formData.TourImComplait.id,
+        objectType: formData.TourImComplait.objectType,
+        userId: formData.TourImComplait.userId,
+        groupId: formData.TourImComplait.groupId,
+        typeId: formData.TourImComplait.typeId,
+        elseTypeReason: formData.TourImComplait.elseTypeReason,
+        description: formData.TourImComplait.description,
+        image: formData.TourImComplait.image,
+        createUserId: formData.TourImComplait.createUserId,
+        createTime: formData.TourImComplait.createTime,
+        updateTime: formData.TourImComplait.updateTime,
+        updateUserId: formData.TourImComplait.updateUserId,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourImComplaitController.update : TourImComplaitController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourImComplaitData().then(res => {
+    userIdWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    groupIdWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    typeId_copyWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    objectTypeWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    let imageDownloadParams = {
+      id: formData.TourImComplait.id,
+      fieldName: 'image',
+      asImage: true
+    };
+    imageWidgetFileList.value = parseUploadData(formData.TourImComplait.image, imageDownloadParams);
+    if (isEdit.value) refreshFormEditTourImComplait();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 409 - 0
src/pages/ImComplaint/formTourImComplaintType.vue

@@ -0,0 +1,409 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourImComplaintTypeRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourImComplaintType()" @reset="resetFormTourImComplaintType">
+        <el-form-item label="类型名称">
+          <el-input
+            class="filter-item"
+            v-model="formFilter.typeNameFilter"
+            type="text"
+            placeholder=""
+            :clearable="true"
+            :show-word-limit="false"
+            maxlength=""
+          />
+        </el-form-item>
+        <el-form-item label="是否启用">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.enableFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="enableFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in enableFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourImComplaintTypeTable"
+      class="page-table"
+      :data="formTourImComplaintTypeTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourImComplaintTypeTableWidgetCurrentPage - 1) * formTourImComplaintTypeTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourImComplaintTypeTableWidget.onSortChange"
+      @refresh="formTourImComplaintTypeTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImComplaintType:formTourImComplaintType:addTourImComplaintType')"
+          @click="onAddTourImComplaintTypeClick()"
+          >
+          新建
+        </el-button>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImComplaintType:formTourImComplaintType:exportTourImComplaintType')"
+          @click="onExportTourImComplaintTypeClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :disabled="!checkPermCodeExist('formTourImComplaintType:formTourImComplaintType:importTourImComplaintType')"
+          :on-change="onImportTourImComplaintTypeClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+              :disabled="!checkPermCodeExist('formTourImComplaintType:formTourImComplaintType:importTourImComplaintType')"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourImComplaintTypeTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="类型名称" field="typeName" />
+      <vxe-column title="类型描述" field="description" />
+      <vxe-column title="是否启用" field="enableDictMap.name" />
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onEditTourImComplaintTypeClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImComplaintType:formTourImComplaintType:editTourImComplaintType')"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onDeleteTourImComplaintTypeClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImComplaintType:formTourImComplaintType:deleteTourImComplaintType')"
+          >
+            删除
+          </el-button>
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourImComplaintTypeTableWidgetTotalCount"
+            :current-page="formTourImComplaintTypeTableWidgetCurrentPage"
+            :page-size="formTourImComplaintTypeTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourImComplaintTypeTableWidget.onCurrentPageChange"
+            @size-change="formTourImComplaintTypeTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourImComplaintType',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImComplaintTypeData } from '@/api/generated/tourImComplaintTypeController';
+import { TourImComplaintTypeController } from '@/api/generated';
+import FormEditTourImComplaintType from '@/pages/ImComplaint/formEditTourImComplaintType.vue';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+  }>(),
+  {
+    subPage: 0,
+  },
+);
+
+const formFilter = reactive({
+  // 类型名称
+  typeNameFilter: undefined,
+  // 是否启用
+  enableFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 类型名称
+  typeNameFilter: undefined,
+  // 是否启用
+  enableFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourImComplaintType();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourImComplaintTypeTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourImComplaintTypeDtoFilter: {
+      typeName: formFilter.typeNameFilter,
+      enable: formFilter.enableFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourImComplaintTypeController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourImComplaintTypeTableVerify = () => {
+  formFilterCopy.typeNameFilter = formFilter.typeNameFilter;
+  formFilterCopy.enableFilter = formFilter.enableFilter;
+  return true;
+};
+/**
+ * 新建
+ */
+const onAddTourImComplaintTypeClick = (row?: TourImComplaintTypeData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  Dialog
+    .show('新建', FormEditTourImComplaintType, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImComplaintTypeTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 编辑
+ */
+const onEditTourImComplaintTypeClick = (row?: TourImComplaintTypeData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  Dialog
+    .show('编辑', FormEditTourImComplaintType, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImComplaintTypeTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourImComplaintTypeClick = (row?: TourImComplaintTypeData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourImComplaintTypeController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourImComplaintTypeClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourImComplaintTypeController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourImComplaintTypeTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourImComplaintTypeClick = (row?: TourImComplaintTypeData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourImComplaintTypeController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourImComplaintTypeTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourImComplaintTypeTableOptions: TableOptions<TourImComplaintTypeData> = {
+  loadTableData: loadFormTourImComplaintTypeTableWidgetData,
+  verifyTableParameter: loadFormTourImComplaintTypeTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourImComplaintTypeTable = ref();
+const formTourImComplaintTypeTableWidget = useTable(formTourImComplaintTypeTableOptions);
+const {
+  dataList: formTourImComplaintTypeTableWidgetDataList,
+  currentPage: formTourImComplaintTypeTableWidgetCurrentPage,
+  pageSize: formTourImComplaintTypeTableWidgetPageSize,
+  totalCount: formTourImComplaintTypeTableWidgetTotalCount,
+} = formTourImComplaintTypeTableWidget;
+/**
+ * 是否启用下拉数据获取函数
+ */
+const loadEnableFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.Enable.getList(),
+  });
+};
+// 是否启用配置参数
+const enableFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadEnableFilterDropdownList,
+  isTree: false,
+};
+// 是否启用下拉组件
+const enableFilterWidget = useDropdown(enableFilterOptions);
+const { dropdownList: enableFilterWidgetDropdownList } = enableFilterWidget
+const refreshFormTourImComplaintType = () => {
+  // 刷新段落
+  formTourImComplaintTypeTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourImComplaintType = () => {
+  formFilter.typeNameFilter = undefined;
+  formFilterCopy.typeNameFilter = undefined;
+  formFilter.enableFilter = undefined;
+  formFilterCopy.enableFilter = undefined;
+  refreshFormTourImComplaintType();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourImComplaintType();
+};
+const formInit = () => {
+  enableFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  refreshFormTourImComplaintType();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script>

+ 506 - 0
src/pages/ImComplaint/formTourImComplait.vue

@@ -0,0 +1,506 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourImComplaitRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourImComplait()" @reset="resetFormTourImComplait">
+        <el-form-item label="举报类型">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.typeIdFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="typeIdFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in typeIdFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="举报对象">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.objectTypeFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="objectTypeFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in objectTypeFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="举报描述">
+          <el-input
+            class="filter-item"
+            v-model="formFilter.descriptionFilter"
+            type="text"
+            placeholder=""
+            :clearable="true"
+            :show-word-limit="false"
+            maxlength=""
+          />
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourImComplaitTable"
+      class="page-table"
+      :data="formTourImComplaitTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourImComplaitTableWidgetCurrentPage - 1) * formTourImComplaitTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourImComplaitTableWidget.onSortChange"
+      @refresh="formTourImComplaitTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImComplait:formTourImComplait:addTourImComplait')"
+          @click="onAddTourImComplaitClick()"
+          >
+          新建
+        </el-button>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImComplait:formTourImComplait:exportTourImComplait')"
+          @click="onExportTourImComplaitClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :disabled="!checkPermCodeExist('formTourImComplait:formTourImComplait:importTourImComplait')"
+          :on-change="onImportTourImComplaitClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+              :disabled="!checkPermCodeExist('formTourImComplait:formTourImComplait:importTourImComplait')"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourImComplaitTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="举报类型" field="typeIdDictMap.name" />
+      <vxe-column title="举报对象类型" field="objectTypeDictMap.name" />
+      <vxe-column title="举报对象名称" field="objectTypeDictMap.name">
+        <template v-slot="scope">
+          <span v-if="scope.row.objectType == 2">
+            <span v-if="scope.row.groupIdDictMap">{{ scope.row.groupIdDictMap.name }}</span>
+          </span>
+          <span v-if="scope.row.objectType == 1">
+            <span v-if="scope.row.userIdDictMap">{{ scope.row.userIdDictMap.name }}</span>
+          <span v-else>-</span>
+        </span>
+        </template>
+      </vxe-column>
+
+      <vxe-column title="举报对象id" field="userIdDictMap.name">
+        <template v-slot="scope">
+    <span v-if="scope.row.objectType == 1">
+      {{ scope.row.userId }}
+    </span>
+          <span v-if="scope.row.objectType == 2">
+      {{ scope.row.groupId }}
+    </span>
+        </template>
+      </vxe-column>
+
+      <vxe-column title="其他违规理由" field="elseTypeReason" />
+      <vxe-column title="举报描述" field="description" />
+      <vxe-column title="举报图片">
+        <template v-slot="scope">
+          <upload-file-list
+            :file-list="
+              parseUploadData(scope.row.image, {
+                id: scope.row.id,
+                fieldName: 'image',
+                asImage: true
+              })
+            "
+            type="card"
+            direction="horizontal"
+            :readonly="true"
+          />
+        </template>
+      </vxe-column>
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onEditTourImComplaitClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImComplait:formTourImComplait:editTourImComplait')"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onDeleteTourImComplaitClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImComplait:formTourImComplait:deleteTourImComplait')"
+          >
+            删除
+          </el-button>
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourImComplaitTableWidgetTotalCount"
+            :current-page="formTourImComplaitTableWidgetCurrentPage"
+            :page-size="formTourImComplaitTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourImComplaitTableWidget.onCurrentPageChange"
+            @size-change="formTourImComplaitTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourImComplait',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImComplaitData } from '@/api/generated/tourImComplaitController';
+import { TourImComplaitController } from '@/api/generated';
+import FormEditTourImComplait from '@/pages/ImComplaint/formEditTourImComplait.vue';
+import "./formTourImComplait.vue";
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const {
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+  }>(),
+  {
+    subPage: 0,
+  },
+);
+
+const formFilter = reactive({
+  // 举报类型
+  typeIdFilter: undefined,
+  // 举报对象
+  objectTypeFilter: undefined,
+  // 举报描述
+  descriptionFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 举报类型
+  typeIdFilter: undefined,
+  // 举报对象
+  objectTypeFilter: undefined,
+  // 举报描述
+  descriptionFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourImComplait();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourImComplaitTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourImComplaitDtoFilter: {
+      typeId: formFilter.typeIdFilter,
+      objectType: formFilter.objectTypeFilter,
+      description: formFilter.descriptionFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourImComplaitController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourImComplaitTableVerify = () => {
+  formFilterCopy.typeIdFilter = formFilter.typeIdFilter;
+  formFilterCopy.objectTypeFilter = formFilter.objectTypeFilter;
+  formFilterCopy.descriptionFilter = formFilter.descriptionFilter;
+  return true;
+};
+/**
+ * 新建
+ */
+const onAddTourImComplaitClick = (row?: TourImComplaitData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  Dialog
+    .show('新建', FormEditTourImComplait, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImComplaitTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 编辑
+ */
+const onEditTourImComplaitClick = (row?: TourImComplaitData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  Dialog
+    .show('编辑', FormEditTourImComplait, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImComplaitTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourImComplaitClick = (row?: TourImComplaitData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourImComplaitController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourImComplaitClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourImComplaitController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourImComplaitTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourImComplaitClick = (row?: TourImComplaitData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourImComplaitController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourImComplaitTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourImComplaitTableOptions: TableOptions<TourImComplaitData> = {
+  loadTableData: loadFormTourImComplaitTableWidgetData,
+  verifyTableParameter: loadFormTourImComplaitTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourImComplaitTable = ref();
+const formTourImComplaitTableWidget = useTable(formTourImComplaitTableOptions);
+const {
+  dataList: formTourImComplaitTableWidgetDataList,
+  currentPage: formTourImComplaitTableWidgetCurrentPage,
+  pageSize: formTourImComplaitTableWidgetPageSize,
+  totalCount: formTourImComplaitTableWidgetTotalCount,
+} = formTourImComplaitTableWidget;
+/**
+ * 举报类型下拉数据获取函数
+ */
+const loadTypeIdFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImComplaintType(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 举报类型配置参数
+const typeIdFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadTypeIdFilterDropdownList,
+  isTree: false,
+};
+// 举报类型下拉组件
+const typeIdFilterWidget = useDropdown(typeIdFilterOptions);
+const { dropdownList: typeIdFilterWidgetDropdownList } = typeIdFilterWidget
+/**
+ * 举报对象下拉数据获取函数
+ */
+const loadObjectTypeFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'ComplaintObjectType', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 举报对象配置参数
+const objectTypeFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadObjectTypeFilterDropdownList,
+  isTree: false,
+};
+// 举报对象下拉组件
+const objectTypeFilterWidget = useDropdown(objectTypeFilterOptions);
+const { dropdownList: objectTypeFilterWidgetDropdownList } = objectTypeFilterWidget
+const refreshFormTourImComplait = () => {
+  // 刷新段落
+  formTourImComplaitTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourImComplait = () => {
+  formFilter.typeIdFilter = undefined;
+  formFilterCopy.typeIdFilter = undefined;
+  formFilter.objectTypeFilter = undefined;
+  formFilterCopy.objectTypeFilter = undefined;
+  formFilter.descriptionFilter = undefined;
+  formFilterCopy.descriptionFilter = undefined;
+  refreshFormTourImComplait();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourImComplait();
+};
+const formInit = () => {
+  typeIdFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  objectTypeFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  refreshFormTourImComplait();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script>

+ 607 - 0
src/pages/ImGroup/formEditTourImGroup.vue

@@ -0,0 +1,607 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourImGroupRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="群主" prop="TourImGroup.leaderId">-->
+<!--              <el-select-->
+<!--                class="input-item"-->
+<!--                v-model="formData.TourImGroup.leaderId"-->
+<!--                placeholder=""-->
+<!--                :clearable="true"-->
+<!--                :filterable="true"-->
+<!--                @visible-change="leaderIdWidget.onVisibleChange"-->
+<!--              >-->
+<!--                <el-option-->
+<!--                  v-for="item in leaderIdWidgetDropdownList"-->
+<!--                  :key="item.id"-->
+<!--                  :label="item.name"-->
+<!--                  :value="item.id"-->
+<!--                />-->
+<!--              </el-select>-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="群聊所属类型" prop="TourImGroup.belongTypeId">-->
+<!--              <el-cascader-->
+<!--                class="input-item"-->
+<!--                v-model="belongTypeIdPath"-->
+<!--                :options="belongTypeIdWidgetDropdownList"-->
+<!--                placeholder=""-->
+<!--                :clearable="true"-->
+<!--                :filterable="true"-->
+<!--                :show-all-levels="false"-->
+<!--                :props="{ value: 'id', label: 'name', children: 'children', checkStrictly: true }"-->
+<!--                @change="onBelongTypeIdValueChange"-->
+<!--              />-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="群聊名称" prop="TourImGroup.groupName">-->
+<!--              <el-input-->
+<!--                class="input-item"-->
+<!--                v-model="formData.TourImGroup.groupName"-->
+<!--                type="text"-->
+<!--                placeholder=""-->
+<!--                :clearable="true"-->
+<!--                :show-word-limit="false"-->
+<!--                maxlength=""-->
+<!--              />-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="群聊头像" prop="TourImGroup.groupAvatar">-->
+<!--              <custom-upload-->
+<!--                v-model="groupAvatarWidgetFileList"-->
+<!--                name="uploadFile"-->
+<!--                :size="layoutStore.defaultFormItemSize"-->
+<!--                type="expand"-->
+<!--                :headers="getUploadHeaders"-->
+<!--                :action="getUploadActionUrl('/admin/app/tourImGroup/upload')"-->
+<!--                :data="{fieldName: 'groupAvatar', asImage: true}"-->
+<!--                :limit="groupAvatarWidgetMaxCount"-->
+<!--                @change="onGroupAvatarChange"-->
+<!--              />-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="群聊描述" prop="TourImGroup.description">-->
+<!--              <el-input-->
+<!--                class="input-item"-->
+<!--                v-model="formData.TourImGroup.description"-->
+<!--                type="text"-->
+<!--                placeholder=""-->
+<!--                :clearable="true"-->
+<!--                :show-word-limit="false"-->
+<!--                maxlength=""-->
+<!--              />-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="是否公开展示" prop="TourImGroup.isPublic">-->
+<!--              <el-select-->
+<!--                class="input-item"-->
+<!--                v-model="formData.TourImGroup.isPublic"-->
+<!--                placeholder=""-->
+<!--                :clearable="true"-->
+<!--                :filterable="true"-->
+<!--                @visible-change="isPublicWidget.onVisibleChange"-->
+<!--              >-->
+<!--                <el-option-->
+<!--                  v-for="item in isPublicWidgetDropdownList"-->
+<!--                  :key="item.id"-->
+<!--                  :label="item.name"-->
+<!--                  :value="item.id"-->
+<!--                />-->
+<!--              </el-select>-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="是否开启群聊邀请确认" prop="TourImGroup.isNeedConfirm">-->
+<!--              <el-select-->
+<!--                class="input-item"-->
+<!--                v-model="formData.TourImGroup.isNeedConfirm"-->
+<!--                placeholder=""-->
+<!--                :clearable="true"-->
+<!--                :filterable="true"-->
+<!--                @visible-change="isNeedConfirmWidget.onVisibleChange"-->
+<!--              >-->
+<!--                <el-option-->
+<!--                  v-for="item in isNeedConfirmWidgetDropdownList"-->
+<!--                  :key="item.id"-->
+<!--                  :label="item.name"-->
+<!--                  :value="item.id"-->
+<!--                />-->
+<!--              </el-select>-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
+<!--          <el-col :span="12">-->
+<!--            <el-form-item label="通知分类 " prop="TourImGroup.noticeType">-->
+<!--              <el-select-->
+<!--                class="input-item"-->
+<!--                v-model="formData.TourImGroup.noticeType"-->
+<!--                placeholder=""-->
+<!--                :clearable="true"-->
+<!--                :filterable="true"-->
+<!--                @visible-change="noticeTypeWidget.onVisibleChange"-->
+<!--              >-->
+<!--                <el-option-->
+<!--                  v-for="item in noticeTypeWidgetDropdownList"-->
+<!--                  :key="item.id"-->
+<!--                  :label="item.name"-->
+<!--                  :value="item.id"-->
+<!--                />-->
+<!--              </el-select>-->
+<!--            </el-form-item>-->
+<!--          </el-col>-->
+          <el-col :span="12">
+            <el-form-item label="是否封禁 " prop="TourImGroup.bannedStatus">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImGroup.bannedStatus"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="bannedStatusWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in bannedStatusWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="热度值" prop="TourImGroup.hotValue">
+              <el-input-number
+                class="input-item"
+                v-model="formData.TourImGroup.hotValue"
+                placeholder=""
+                :clearable="true"
+                :step="1"
+                :controls="true"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourImGroupClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourImGroup',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImMemberData } from '@/api/generated/tourImMemberController';
+import { TourImGroupInvitationData } from '@/api/generated/tourImGroupInvitationController';
+import { TourImMessageData } from '@/api/generated/tourImMessageController';
+import { TourImGroupData } from '@/api/generated/tourImGroupController';
+import { TourImMemberController, TourImGroupInvitationController, TourImMessageController, TourImGroupController } from '@/api/generated';
+import TourUser from "@/types/table/tourUser";
+import TourUserController from '@/api/generated';
+
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const {
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourImGroupRef = ref();
+// 表单数据定义
+type FormEditTourImGroupData = {
+  TourImGroup: TourImGroupData;
+};
+// 表单数据
+const formData = reactive<FormEditTourImGroupData>({
+  TourImGroup: {
+    // id
+    id: undefined,
+    // 群聊所属类型id
+    belongTypeId: undefined,
+    // 群主id
+    leaderId: undefined,
+    // 群聊名称
+    groupName: undefined,
+    // 群聊头像
+    groupAvatar: undefined,
+    // 群聊描述
+    description: undefined,
+    // 是否公开展示 0隐藏 1公开
+    isPublic: undefined,
+    // 是否开启群聊邀请确认 0不开启 1开启
+    isNeedConfirm: undefined,
+    // 通知分类 0单聊 1群聊
+    noticeType: undefined,
+    // 是否封禁 0封禁 1正常
+    bannedStatus: undefined,
+    // 热度值
+    hotValue: undefined,
+    // 删除标记 1正常 -1删除
+    dataState: undefined,
+    // 创建人id
+    createUserId: undefined,
+    // 创建时间
+    createTime: undefined,
+    // 更新时间
+    updateTime: undefined,
+    // 更新人id
+    updateUserId: undefined,
+    // 聊天去聊邀请数据
+    tourImGroupInvitationList: [],
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourImGroup.groupName': [
+    {required: true, message: '请输入群聊名称', trigger: 'blur'}
+  ],
+  'TourImGroup.groupAvatar': [
+    {required: true, message: '请输入群聊头像', trigger: 'blur'}
+  ],
+  'TourImGroup.belongTypeId': [
+    {required: true, message: '请输入群聊所属类型', trigger: 'blur'}
+  ],
+  'TourImGroup.leaderId': [
+    {required: true, message: '请输入群主', trigger: 'blur'}
+  ],
+  'TourImGroup.isNeedConfirm': [
+  ],
+  'TourImGroup.description': [
+  ],
+  'TourImGroup.isPublic': [
+  ],
+  'TourImGroup.bannedStatus': [
+    {required: true, message: '请输入是否封禁 ', trigger: 'blur'}
+  ],
+  'TourImGroup.noticeType': [
+    {required: true, message: '请输入通知分类 ', trigger: 'blur'}
+  ],
+  'TourImGroup.hotValue': [
+    {required: true, message: '请输入热度值', trigger: 'blur'}
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourImGroupData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourImGroup = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourImGroupController.view(params).then(res => {
+      formData.TourImGroup = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 群主下拉数据获取函数
+ */
+const loadLeaderIdDropdownList = (): Promise<ListData<TourUser>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    TourUserController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 群主配置参数
+const leaderIdOptions: DropdownOptions<TourUser> = {
+  loadData: loadLeaderIdDropdownList,
+  isTree: false,
+};
+// 群主下拉组件
+const leaderIdWidget = useDropdown(leaderIdOptions);
+const { dropdownList: leaderIdWidgetDropdownList } = leaderIdWidget
+/**
+ * 群聊所属类型下拉数据获取函数
+ */
+const loadBelongTypeIdDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroupType(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 群聊所属类型选中值改变
+ */
+const onBelongTypeIdValueChange = (value) => {
+  formData.TourImGroup.belongTypeId = Array.isArray(value) ? value[value.length - 1] : value;
+};
+// 群聊所属类型配置参数
+const belongTypeIdOptions: DropdownOptions<DictData> = {
+  loadData: loadBelongTypeIdDropdownList,
+  isTree: true,
+};
+// 群聊所属类型选中数据
+const belongTypeIdPath = ref<Array<string | number>>([]);
+// 群聊所属类型下拉组件
+const belongTypeIdWidget = useDropdown(belongTypeIdOptions);
+const { dropdownList: belongTypeIdWidgetDropdownList } = belongTypeIdWidget
+/**
+ * 群聊头像上传文件改变
+ */
+const onGroupAvatarChange = val => {
+  formData.TourImGroup.groupAvatar = fileListToJson(val);
+};
+// 群聊头像上传文件组件
+const groupAvatarWidget = useUploadWidget(1);
+const { fileList: groupAvatarWidgetFileList, maxCount: groupAvatarWidgetMaxCount } = groupAvatarWidget;
+/**
+ * 是否公开展示下拉数据获取函数
+ */
+const loadIsPublicDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 是否公开展示配置参数
+const isPublicOptions: DropdownOptions<DictData> = {
+  loadData: loadIsPublicDropdownList,
+  isTree: false,
+};
+// 是否公开展示下拉组件
+const isPublicWidget = useDropdown(isPublicOptions);
+const { dropdownList: isPublicWidgetDropdownList } = isPublicWidget
+/**
+ * 是否开启群聊邀请确认下拉数据获取函数
+ */
+const loadIsNeedConfirmDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 是否开启群聊邀请确认配置参数
+const isNeedConfirmOptions: DropdownOptions<DictData> = {
+  loadData: loadIsNeedConfirmDropdownList,
+  isTree: false,
+};
+// 是否开启群聊邀请确认下拉组件
+const isNeedConfirmWidget = useDropdown(isNeedConfirmOptions);
+const { dropdownList: isNeedConfirmWidgetDropdownList } = isNeedConfirmWidget
+/**
+ * 通知分类 下拉数据获取函数
+ */
+const loadNoticeTypeDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'TourImSingleOrGroup', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 通知分类 配置参数
+const noticeTypeOptions: DropdownOptions<DictData> = {
+  loadData: loadNoticeTypeDropdownList,
+  isTree: false,
+};
+// 通知分类 下拉组件
+const noticeTypeWidget = useDropdown(noticeTypeOptions);
+const { dropdownList: noticeTypeWidgetDropdownList } = noticeTypeWidget
+/**
+ * 是否封禁 下拉数据获取函数
+ */
+const loadBannedStatusDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 是否封禁 配置参数
+const bannedStatusOptions: DropdownOptions<DictData> = {
+  loadData: loadBannedStatusDropdownList,
+  isTree: false,
+};
+// 是否封禁 下拉组件
+const bannedStatusWidget = useDropdown(bannedStatusOptions);
+const { dropdownList: bannedStatusWidgetDropdownList } = bannedStatusWidget
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourImGroup = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourImGroup = () => {
+  refreshFormEditTourImGroup();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourImGroup();
+};
+/**
+ * 保存
+ */
+const onSubmitTourImGroupClick = () => {
+  formEditTourImGroupRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourImGroup
+      };
+    retFormData.leaderIdDictMap = findItemFromList(leaderIdWidgetDropdownList.value, retFormData.leaderId, 'id');
+    retFormData.belongTypeIdDictMap = findTreeNode(belongTypeIdWidgetDropdownList.value, retFormData.belongTypeId, 'id');
+    retFormData.isPublicDictMap = findItemFromList(isPublicWidgetDropdownList.value, retFormData.isPublic, 'id');
+    retFormData.isNeedConfirmDictMap = findItemFromList(isNeedConfirmWidgetDropdownList.value, retFormData.isNeedConfirm, 'id');
+    retFormData.noticeTypeDictMap = findItemFromList(noticeTypeWidgetDropdownList.value, retFormData.noticeType, 'id');
+    retFormData.bannedStatusDictMap = findItemFromList(bannedStatusWidgetDropdownList.value, retFormData.bannedStatus, 'id');
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourImGroupDto: {
+        id: formData.TourImGroup.id,
+        noticeType: formData.TourImGroup.noticeType,
+        belongTypeId: formData.TourImGroup.belongTypeId,
+        leaderId: formData.TourImGroup.leaderId,
+        groupName: formData.TourImGroup.groupName,
+        groupAvatar: formData.TourImGroup.groupAvatar,
+        description: formData.TourImGroup.description,
+        isPublic: formData.TourImGroup.isPublic,
+        isNeedConfirm: formData.TourImGroup.isNeedConfirm,
+        bannedStatus: formData.TourImGroup.bannedStatus,
+        hotValue: formData.TourImGroup.hotValue,
+        createUserId: formData.TourImGroup.createUserId,
+        createTime: formData.TourImGroup.createTime,
+        updateTime: formData.TourImGroup.updateTime,
+        updateUserId: formData.TourImGroup.updateUserId,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourImGroupController.update : TourImGroupController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourImGroupData().then(res => {
+    leaderIdWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    belongTypeIdWidget.onVisibleChange(true).then(res => {
+      // TODO: 获取级联选中路径
+      belongTypeIdPath.value = findTreeNodePath(res, formData.TourImGroup.belongTypeId);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+    let groupAvatarDownloadParams = {
+      id: formData.TourImGroup.id,
+      fieldName: 'groupAvatar',
+      asImage: true
+    };
+    groupAvatarWidgetFileList.value = parseUploadData(formData.TourImGroup.groupAvatar, groupAvatarDownloadParams);
+    isPublicWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    isNeedConfirmWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    noticeTypeWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    bannedStatusWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    if (isEdit.value) refreshFormEditTourImGroup();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 278 - 0
src/pages/ImGroup/formEditTourImGroupInvitation.vue

@@ -0,0 +1,278 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourImGroupInvitationRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="邀请状态 " prop="TourImGroupInvitation.status">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImGroupInvitation.status"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="statusWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in statusWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourImGroupInvitationClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourImGroupInvitation',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImGroupInvitationData } from '@/api/generated/tourImGroupInvitationController';
+import { TourImGroupInvitationController } from '@/api/generated';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourImGroupInvitationRef = ref();
+// 表单数据定义
+type FormEditTourImGroupInvitationData = {
+  TourImGroupInvitation: TourImGroupInvitationData;
+};
+// 表单数据
+const formData = reactive<FormEditTourImGroupInvitationData>({
+  TourImGroupInvitation: {
+    // id
+    id: undefined,
+    // 聊天群组id
+    groupId: undefined,
+    // 邀请人id
+    inviter: undefined,
+    // 被邀请人id
+    invitee: undefined,
+    // 邀请状态 1待确认 2同意 3不同意
+    status: undefined,
+    // 删除标记 1正常 -1删除
+    dataState: undefined,
+    // 创建人id
+    createUserId: undefined,
+    // 创建时间
+    createTime: undefined,
+    // 更新时间
+    updateTime: undefined,
+    // 更新人id
+    updateUserId: undefined,
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourImGroupInvitation.status': [
+    {required: true, message: '请输入邀请状态 ', trigger: 'blur'}
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourImGroupInvitationData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourImGroupInvitation = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourImGroupInvitationController.view(params).then(res => {
+      formData.TourImGroupInvitation = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 邀请状态 下拉数据获取函数
+ */
+const loadStatusDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'GroupInvitationStatus', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 邀请状态 配置参数
+const statusOptions: DropdownOptions<DictData> = {
+  loadData: loadStatusDropdownList,
+  isTree: false,
+};
+// 邀请状态 下拉组件
+const statusWidget = useDropdown(statusOptions);
+const { dropdownList: statusWidgetDropdownList } = statusWidget
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourImGroupInvitation = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourImGroupInvitation = () => {
+  refreshFormEditTourImGroupInvitation();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourImGroupInvitation();
+};
+/**
+ * 保存
+ */
+const onSubmitTourImGroupInvitationClick = () => {
+  formEditTourImGroupInvitationRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourImGroupInvitation
+      };
+    retFormData.statusDictMap = findItemFromList(statusWidgetDropdownList.value, retFormData.status, 'id');
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourImGroupInvitationDto: {
+        id: formData.TourImGroupInvitation.id,
+        groupId: formData.TourImGroupInvitation.groupId,
+        inviter: formData.TourImGroupInvitation.inviter,
+        invitee: formData.TourImGroupInvitation.invitee,
+        status: formData.TourImGroupInvitation.status,
+        createUserId: formData.TourImGroupInvitation.createUserId,
+        createTime: formData.TourImGroupInvitation.createTime,
+        updateTime: formData.TourImGroupInvitation.updateTime,
+        updateUserId: formData.TourImGroupInvitation.updateUserId,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourImGroupInvitationController.update : TourImGroupInvitationController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourImGroupInvitationData().then(res => {
+    statusWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    if (isEdit.value) refreshFormEditTourImGroupInvitation();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 376 - 0
src/pages/ImGroup/formEditTourImGroupType.vue

@@ -0,0 +1,376 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourImGroupTypeRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="一级类型" prop="TourImGroupType.parentId">
+              <el-cascader
+                class="input-item"
+                v-model="parentIdPath"
+                :options="parentIdWidgetDropdownList"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                :show-all-levels="false"
+                :props="{ value: 'id', label: 'name', children: 'children', checkStrictly: true }"
+                @change="onParentIdValueChange"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="二级类型" prop="TourImGroupType.typeName">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImGroupType.typeName"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="类型图标" prop="TourImGroupType.typeIcon">
+              <custom-upload
+                v-model="typeIconWidgetFileList"
+                name="uploadFile"
+                :size="layoutStore.defaultFormItemSize"
+                type="expand"
+                :headers="getUploadHeaders"
+                :action="getUploadActionUrl('/admin/app/tourImGroupType/upload')"
+                :data="{fieldName: 'typeIcon', asImage: true}"
+                :limit="typeIconWidgetMaxCount"
+                @change="onTypeIconChange"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="是否启用 " prop="TourImGroupType.enable">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImGroupType.enable"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="enableWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in enableWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourImGroupTypeClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourImGroupType',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImGroupTypeData } from '@/api/generated/tourImGroupTypeController';
+import { TourImGroupTypeController } from '@/api/generated';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourImGroupTypeRef = ref();
+// 表单数据定义
+type FormEditTourImGroupTypeData = {
+  TourImGroupType: TourImGroupTypeData;
+};
+// 表单数据
+const formData = reactive<FormEditTourImGroupTypeData>({
+  TourImGroupType: {
+    // id
+    id: undefined,
+    // 父级id
+    parentId: undefined,
+    // 类型名称
+    typeName: undefined,
+    // 类型图标
+    typeIcon: undefined,
+    // 是否启用 0否 1是
+    enable: undefined,
+    // 删除标记 1正常 -1删除
+    dataState: undefined,
+    // 创建人id
+    createUserId: undefined,
+    // 创建时间
+    createTime: undefined,
+    // 更新时间
+    updateTime: undefined,
+    // 更新人id
+    updateUserId: undefined,
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourImGroupType.enable': [
+  ],
+  'TourImGroupType.parentId': [
+  ],
+  'TourImGroupType.typeIcon': [
+  ],
+  'TourImGroupType.typeName': [
+    {required: true, message: '请输入二级类型', trigger: 'blur'}
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourImGroupTypeData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourImGroupType = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourImGroupTypeController.view(params).then(res => {
+      formData.TourImGroupType = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 一级类型下拉数据获取函数
+ */
+const loadParentIdDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroupType(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 一级类型选中值改变
+ */
+const onParentIdValueChange = (value) => {
+  formData.TourImGroupType.parentId = Array.isArray(value) ? value[value.length - 1] : value;
+};
+// 一级类型配置参数
+const parentIdOptions: DropdownOptions<DictData> = {
+  loadData: loadParentIdDropdownList,
+  isTree: true,
+};
+// 一级类型选中数据
+const parentIdPath = ref<Array<string | number>>([]);
+// 一级类型下拉组件
+const parentIdWidget = useDropdown(parentIdOptions);
+const { dropdownList: parentIdWidgetDropdownList } = parentIdWidget
+/**
+ * 类型图标上传文件改变
+ */
+const onTypeIconChange = val => {
+  formData.TourImGroupType.typeIcon = fileListToJson(val);
+};
+// 类型图标上传文件组件
+const typeIconWidget = useUploadWidget(1);
+const { fileList: typeIconWidgetFileList, maxCount: typeIconWidgetMaxCount } = typeIconWidget;
+/**
+ * 是否启用 下拉数据获取函数
+ */
+const loadEnableDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.Enable.getList(),
+  });
+};
+// 是否启用 配置参数
+const enableOptions: DropdownOptions<DictData> = {
+  loadData: loadEnableDropdownList,
+  isTree: false,
+};
+// 是否启用 下拉组件
+const enableWidget = useDropdown(enableOptions);
+const { dropdownList: enableWidgetDropdownList } = enableWidget
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourImGroupType = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourImGroupType = () => {
+  refreshFormEditTourImGroupType();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourImGroupType();
+};
+/**
+ * 保存
+ */
+const onSubmitTourImGroupTypeClick = () => {
+  formEditTourImGroupTypeRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourImGroupType
+      };
+    retFormData.parentIdDictMap = findTreeNode(parentIdWidgetDropdownList.value, retFormData.parentId, 'id');
+    retFormData.enableDictMap = findItemFromList(enableWidgetDropdownList.value, retFormData.enable, 'id');
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourImGroupTypeDto: {
+        id: formData.TourImGroupType.id,
+        parentId: formData.TourImGroupType.parentId,
+        typeName: formData.TourImGroupType.typeName,
+        typeIcon: formData.TourImGroupType.typeIcon,
+        enable: formData.TourImGroupType.enable,
+        createUserId: formData.TourImGroupType.createUserId,
+        createTime: formData.TourImGroupType.createTime,
+        updateTime: formData.TourImGroupType.updateTime,
+        updateUserId: formData.TourImGroupType.updateUserId,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourImGroupTypeController.update : TourImGroupTypeController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourImGroupTypeData().then(res => {
+    parentIdWidget.onVisibleChange(true).then(res => {
+      // TODO: 获取级联选中路径
+      parentIdPath.value = findTreeNodePath(res, formData.TourImGroupType.parentId);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+    let typeIconDownloadParams = {
+      id: formData.TourImGroupType.id,
+      fieldName: 'typeIcon',
+      asImage: true
+    };
+    typeIconWidgetFileList.value = parseUploadData(formData.TourImGroupType.typeIcon, typeIconDownloadParams);
+    enableWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    if (isEdit.value) refreshFormEditTourImGroupType();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 260 - 0
src/pages/ImGroup/formEditTourImMember.vue

@@ -0,0 +1,260 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourImMemberRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="删除标记 " prop="TourImMember.dataState">
+              <el-input-number
+                class="input-item"
+                v-model="formData.TourImMember.dataState"
+                placeholder=""
+                :clearable="true"
+                :step="1"
+                :controls="true"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourImMemberClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourImMember',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImMemberData } from '@/api/generated/tourImMemberController';
+import { TourImMemberController } from '@/api/generated';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourImMemberRef = ref();
+// 表单数据定义
+type FormEditTourImMemberData = {
+  TourImMember: TourImMemberData;
+};
+// 表单数据
+const formData = reactive<FormEditTourImMemberData>({
+  TourImMember: {
+    // id
+    id: undefined,
+    // 聊天群组id
+    groupId: undefined,
+    // 成员角色 1群主 2管理员 3普通成员
+    groupRole: undefined,
+    // 用户id
+    userId: undefined,
+    // 会话是否显示(每个群在聊天列表要不要展示) 0不显示 1显示
+    isShow: undefined,
+    // 会话是否置顶 (每个群在聊天列表要不要置顶)  0不置顶 1 置顶
+    isTop: undefined,
+    // 会话是否开启免打扰 0不开启免打扰 1 开启免打扰
+    isNotDisturb: undefined,
+    // 自己在群里的昵称
+    groupNickname: undefined,
+    // 群备注
+    groupRemark: undefined,
+    // 删除标记 1正常 -1删除
+    dataState: undefined,
+    // 聊天背景
+    groupBackImage: undefined,
+    // 创建人id
+    createUserId: undefined,
+    // 创建时间
+    createTime: undefined,
+    // 更新时间
+    updateTime: undefined,
+    // 更新人id
+    updateUserId: undefined,
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourImMember.dataState': [
+    {required: true, message: '请输入删除标记 ', trigger: 'blur'}
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourImMemberData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourImMember = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourImMemberController.view(params).then(res => {
+      formData.TourImMember = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourImMember = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourImMember = () => {
+  refreshFormEditTourImMember();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourImMember();
+};
+/**
+ * 保存
+ */
+const onSubmitTourImMemberClick = () => {
+  formEditTourImMemberRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourImMember
+      };
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourImMemberDto: {
+        id: formData.TourImMember.id,
+        groupId: formData.TourImMember.groupId,
+        groupRole: formData.TourImMember.groupRole,
+        isShow: formData.TourImMember.isShow,
+        isTop: formData.TourImMember.isTop,
+        isNotDisturb: formData.TourImMember.isNotDisturb,
+        groupNickname: formData.TourImMember.groupNickname,
+        groupRemark: formData.TourImMember.groupRemark,
+        createUserId: formData.TourImMember.createUserId,
+        createTime: formData.TourImMember.createTime,
+        updateTime: formData.TourImMember.updateTime,
+        updateUserId: formData.TourImMember.updateUserId,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourImMemberController.update : TourImMemberController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourImMemberData().then(res => {
+    if (isEdit.value) refreshFormEditTourImMember();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 524 - 0
src/pages/ImGroup/formEditTourImMessage.vue

@@ -0,0 +1,524 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourImMessageRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="通知消息类型 " prop="TourImMessage.noticeType">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImMessage.noticeType"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="noticeType_copyWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in noticeType_copyWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="消息内容" prop="TourImMessage.messageContent">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImMessage.messageContent"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="消息类型" prop="TourImMessage.messageType">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImMessage.messageType"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="messageTypeWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in messageTypeWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="聊天群组" prop="TourImMessage.groupId">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImMessage.groupId"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="groupIdWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in groupIdWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="消息置顶/公告" prop="TourImMessage.isTop">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImMessage.isTop"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="isTopWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in isTopWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="撤回标记 " prop="TourImMessage.revocationTag">
+              <el-select
+                class="input-item"
+                v-model="formData.TourImMessage.revocationTag"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="revocationTagWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in revocationTagWidgetDropdownList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="已经删除该条消息的用户" prop="TourImMessage.deletedByUsers">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImMessage.deletedByUsers"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="已读该条消息的用户" prop="TourImMessage.readByUsers">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImMessage.readByUsers"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="删除标记" prop="TourImMessage.dataState">
+              <el-input-number
+                class="input-item"
+                v-model="formData.TourImMessage.dataState"
+                placeholder=""
+                :clearable="true"
+                :step="1"
+                :controls="true"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourImMessageClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourImMessage',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImMessageData } from '@/api/generated/tourImMessageController';
+import { TourImMessageController } from '@/api/generated';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourImMessageRef = ref();
+// 表单数据定义
+type FormEditTourImMessageData = {
+  TourImMessage: TourImMessageData;
+};
+// 表单数据
+const formData = reactive<FormEditTourImMessageData>({
+  TourImMessage: {
+    // id
+    id: undefined,
+    // 消息内容
+    messageContent: undefined,
+    // 消息类型 0文字 1文件 2系统消息
+    messageType: undefined,
+    // 聊天群组id
+    groupId: undefined,
+    // 通知消息类型 1.单聊消息 2 群聊消息 3系统消息 4关注信息 5点赞 6评论 7评论区艾特 8文章内艾特 9访问
+    noticeType: undefined,
+    // 消息置顶/公告 0不置顶 1置顶
+    isTop: undefined,
+    // 撤回标记 0不撤回 1撤回
+    revocationTag: undefined,
+    // 已经删除该条消息的用户
+    deletedByUsers: undefined,
+    // 已读该条消息的用户
+    readByUsers: undefined,
+    // 删除标记 1正常 -1删除
+    dataState: undefined,
+    // 创建人id
+    createUserId: undefined,
+    // 创建时间
+    createTime: undefined,
+    // 更新时间
+    updateTime: undefined,
+    // 更新人id
+    updateUserId: undefined,
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourImMessage.noticeType': [
+    {required: true, message: '请输入通知消息类型 ', trigger: 'blur'}
+  ],
+  'TourImMessage.messageContent': [
+    {required: true, message: '请输入消息内容', trigger: 'blur'}
+  ],
+  'TourImMessage.messageType': [
+    {required: true, message: '请输入消息类型', trigger: 'blur'}
+  ],
+  'TourImMessage.groupId': [
+    {required: true, message: '请输入聊天群组', trigger: 'blur'}
+  ],
+  'TourImMessage.isTop': [
+    {required: true, message: '请输入消息置顶/公告', trigger: 'blur'}
+  ],
+  'TourImMessage.readByUsers': [
+  ],
+  'TourImMessage.dataState': [
+    {required: true, message: '请输入删除标记', trigger: 'blur'}
+  ],
+  'TourImMessage.revocationTag': [
+    {required: true, message: '请输入撤回标记 ', trigger: 'blur'}
+  ],
+  'TourImMessage.deletedByUsers': [
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourImMessageData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourImMessage = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourImMessageController.view(params).then(res => {
+      formData.TourImMessage = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 通知消息类型 下拉数据获取函数
+ */
+const loadNoticeType_copyDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'TourImNoticeType', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 通知消息类型 配置参数
+const noticeType_copyOptions: DropdownOptions<DictData> = {
+  loadData: loadNoticeType_copyDropdownList,
+  isTree: false,
+};
+// 通知消息类型 下拉组件
+const noticeType_copyWidget = useDropdown(noticeType_copyOptions);
+const { dropdownList: noticeType_copyWidgetDropdownList } = noticeType_copyWidget
+/**
+ * 消息类型下拉数据获取函数
+ */
+const loadMessageTypeDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'MessageType', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 消息类型配置参数
+const messageTypeOptions: DropdownOptions<DictData> = {
+  loadData: loadMessageTypeDropdownList,
+  isTree: false,
+};
+// 消息类型下拉组件
+const messageTypeWidget = useDropdown(messageTypeOptions);
+const { dropdownList: messageTypeWidgetDropdownList } = messageTypeWidget
+/**
+ * 聊天群组下拉数据获取函数
+ */
+const loadGroupIdDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroup(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 聊天群组配置参数
+const groupIdOptions: DropdownOptions<DictData> = {
+  loadData: loadGroupIdDropdownList,
+  isTree: false,
+};
+// 聊天群组下拉组件
+const groupIdWidget = useDropdown(groupIdOptions);
+const { dropdownList: groupIdWidgetDropdownList } = groupIdWidget
+/**
+ * 消息置顶/公告下拉数据获取函数
+ */
+const loadIsTopDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 消息置顶/公告配置参数
+const isTopOptions: DropdownOptions<DictData> = {
+  loadData: loadIsTopDropdownList,
+  isTree: false,
+};
+// 消息置顶/公告下拉组件
+const isTopWidget = useDropdown(isTopOptions);
+const { dropdownList: isTopWidgetDropdownList } = isTopWidget
+/**
+ * 撤回标记 下拉数据获取函数
+ */
+const loadRevocationTagDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 撤回标记 配置参数
+const revocationTagOptions: DropdownOptions<DictData> = {
+  loadData: loadRevocationTagDropdownList,
+  isTree: false,
+};
+// 撤回标记 下拉组件
+const revocationTagWidget = useDropdown(revocationTagOptions);
+const { dropdownList: revocationTagWidgetDropdownList } = revocationTagWidget
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourImMessage = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourImMessage = () => {
+  refreshFormEditTourImMessage();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourImMessage();
+};
+/**
+ * 保存
+ */
+const onSubmitTourImMessageClick = () => {
+  formEditTourImMessageRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourImMessage
+      };
+    retFormData.noticeTypeDictMap = findItemFromList(noticeType_copyWidgetDropdownList.value, retFormData.noticeType, 'id');
+    retFormData.messageTypeDictMap = findItemFromList(messageTypeWidgetDropdownList.value, retFormData.messageType, 'id');
+    retFormData.groupIdDictMap = findItemFromList(groupIdWidgetDropdownList.value, retFormData.groupId, 'id');
+    retFormData.isTopDictMap = findItemFromList(isTopWidgetDropdownList.value, retFormData.isTop, 'id');
+    retFormData.revocationTagDictMap = findItemFromList(revocationTagWidgetDropdownList.value, retFormData.revocationTag, 'id');
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourImMessageDto: {
+        id: formData.TourImMessage.id,
+        messageContent: formData.TourImMessage.messageContent,
+        messageType: formData.TourImMessage.messageType,
+        groupId: formData.TourImMessage.groupId,
+        isTop: formData.TourImMessage.isTop,
+        revocationTag: formData.TourImMessage.revocationTag,
+        deletedByUsers: formData.TourImMessage.deletedByUsers,
+        readByUsers: formData.TourImMessage.readByUsers,
+        createUserId: formData.TourImMessage.createUserId,
+        createTime: formData.TourImMessage.createTime,
+        updateTime: formData.TourImMessage.updateTime,
+        updateUserId: formData.TourImMessage.updateUserId,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourImMessageController.update : TourImMessageController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourImMessageData().then(res => {
+    noticeType_copyWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    messageTypeWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    groupIdWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    isTopWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    revocationTagWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    if (isEdit.value) refreshFormEditTourImMessage();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 789 - 0
src/pages/ImGroup/formTourImGroup.vue

@@ -0,0 +1,789 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourImGroupRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourImGroup()" @reset="resetFormTourImGroup">
+<!--        <el-form-item label="群聊所属类型">-->
+<!--          <el-cascader-->
+<!--            class="filter-item"-->
+<!--            v-model="belongTypeIdFilterPath"-->
+<!--            :options="belongTypeIdFilterWidgetDropdownList"-->
+<!--            placeholder=""-->
+<!--            :clearable="true"-->
+<!--            :filterable="true"-->
+<!--            :show-all-levels="false"-->
+<!--            :props="{ value: 'id', label: 'name', children: 'children', checkStrictly: true }"-->
+<!--            @change="onBelongTypeIdFilterValueChange"-->
+<!--          />-->
+<!--        </el-form-item>-->
+        <el-form-item label="群主">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.leaderIdFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="leaderIdFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in leaderIdFilterWidgetDropdownList"
+              :key="item.userId"
+              :label="item.showName"
+              :value="item.userId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="群聊名称">
+          <el-input
+            class="filter-item"
+            v-model="formFilter.groupNameFilter"
+            type="text"
+            placeholder=""
+            :clearable="true"
+            :show-word-limit="false"
+            maxlength=""
+          />
+        </el-form-item>
+<!--        <el-form-item label="群聊描述">-->
+<!--          <el-input-->
+<!--            class="filter-item"-->
+<!--            v-model="formFilter.descriptionFilter"-->
+<!--            type="text"-->
+<!--            placeholder=""-->
+<!--            :clearable="true"-->
+<!--            :show-word-limit="false"-->
+<!--            maxlength=""-->
+<!--          />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="单聊群聊">-->
+<!--          <el-select-->
+<!--            class="filter-item"-->
+<!--            v-model="formFilter.noticeTypeFilter"-->
+<!--            placeholder=""-->
+<!--            :clearable="true"-->
+<!--            :filterable="true"-->
+<!--            @visible-change="noticeTypeFilterWidget.onVisibleChange"-->
+<!--          >-->
+<!--            <el-option-->
+<!--              v-for="item in noticeTypeFilterWidgetDropdownList"-->
+<!--              :key="item.id"-->
+<!--              :label="item.name"-->
+<!--              :value="item.id"-->
+<!--            />-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="是否公开展示">-->
+<!--          <el-select-->
+<!--            class="filter-item"-->
+<!--            v-model="formFilter.isPublicFilter"-->
+<!--            placeholder=""-->
+<!--            :clearable="true"-->
+<!--            :filterable="true"-->
+<!--            @visible-change="isPublicFilterWidget.onVisibleChange"-->
+<!--          >-->
+<!--            <el-option-->
+<!--              v-for="item in isPublicFilterWidgetDropdownList"-->
+<!--              :key="item.id"-->
+<!--              :label="item.name"-->
+<!--              :value="item.id"-->
+<!--            />-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="是否开启群聊邀请">-->
+<!--          <el-select-->
+<!--            class="filter-item"-->
+<!--            v-model="formFilter.isNeedConfirmFilter"-->
+<!--            placeholder=""-->
+<!--            :clearable="true"-->
+<!--            :filterable="true"-->
+<!--            @visible-change="isNeedConfirmFilterWidget.onVisibleChange"-->
+<!--          >-->
+<!--            <el-option-->
+<!--              v-for="item in isNeedConfirmFilterWidgetDropdownList"-->
+<!--              :key="item.id"-->
+<!--              :label="item.name"-->
+<!--              :value="item.id"-->
+<!--            />-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
+        <el-form-item label="是否封禁 ">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.bannedStatusFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="bannedStatusFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in bannedStatusFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="热度值">
+          <input-number-range
+            class="filter-item"
+            v-model="formFilter.hotValueFilter"
+            startPlaceholder=""
+            endPlaceholder=""
+            :step="1"
+          />
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourImGroupTable"
+      class="page-table"
+      :data="formTourImGroupTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourImGroupTableWidgetCurrentPage - 1) * formTourImGroupTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourImGroupTableWidget.onSortChange"
+      @refresh="formTourImGroupTableWidget.refreshTable()"
+    >
+      <template #operator>
+<!--        <el-button-->
+<!--          type="primary"-->
+<!--          :size="layoutStore.defaultFormItemSize"-->
+<!--          @click="onAddTourImGroupClick()"-->
+<!--          >-->
+<!--          新建-->
+<!--        </el-button>-->
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          @click="onExportTourImGroupClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :on-change="onImportTourImGroupClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourImGroupTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="群聊类型" field="belongTypeIdDictMap.name" />
+<!--      <vxe-column title="单聊群聊" field="belongTypeIdDictMap.name" />-->
+      <vxe-column title="群主" field="leaderIdDictMap.name" />
+      <vxe-column title="群聊名称" field="groupName" />
+      <vxe-column title="群聊头像">
+        <template v-slot="scope">
+          <upload-file-list
+            :file-list="
+              parseUploadData(scope.row.groupAvatar, {
+                id: scope.row.id,
+                fieldName: 'groupAvatar',
+                asImage: true
+              })
+            "
+            type="card"
+            direction="horizontal"
+            :readonly="true"
+          />
+        </template>
+      </vxe-column>
+      <vxe-column title="群聊描述" field="description" />
+<!--      <vxe-column title="是否公开展示" field="isPublicDictMap.name" />-->
+<!--      <vxe-column title="是否开启群聊邀请确认" field="isNeedConfirmDictMap.name" />-->
+<!--      <vxe-column title="通知分类 " field="noticeTypeDictMap.name" />-->
+      <vxe-column title="是否封禁 " field="bannedStatusDictMap.name" />
+      <vxe-column title="热度值" field="hotValue" />
+<!--      <vxe-column title="创建人id" field="createUserId" />-->
+      <vxe-column title="创建时间" field="createTime">
+        <template v-slot="scope">
+          <span>{{formatDateByStatsType(scope.row.createTime, 'day')}}</span>
+        </template>
+      </vxe-column>
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+<!--          <el-button-->
+<!--            link-->
+<!--            type="primary"-->
+<!--            :size="layoutStore.defaultFormItemSize"-->
+<!--            @click.stop="onListTourImGroupInvitationClick(scope.row)"-->
+<!--          >-->
+<!--            聊天去聊邀请-->
+<!--          </el-button>-->
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onListTourImMemberClick(scope.row)"
+          >
+            聊天群聊成员
+          </el-button>
+<!--          <el-button-->
+<!--            link-->
+<!--            type="primary"-->
+<!--            :size="layoutStore.defaultFormItemSize"-->
+<!--            @click.stop="onListTourImMessageClick(scope.row)"-->
+<!--          >-->
+<!--            聊天群聊消息-->
+<!--          </el-button>-->
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onEditTourImGroupClick(scope.row)"
+          >
+            编辑
+          </el-button>
+<!--          <el-button-->
+<!--            link-->
+<!--            type="primary"-->
+<!--            :size="layoutStore.defaultFormItemSize"-->
+<!--            @click.stop="onDeleteTourImGroupClick(scope.row)"-->
+<!--          >-->
+<!--            删除-->
+<!--          </el-button>-->
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourImGroupTableWidgetTotalCount"
+            :current-page="formTourImGroupTableWidgetCurrentPage"
+            :page-size="formTourImGroupTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourImGroupTableWidget.onCurrentPageChange"
+            @size-change="formTourImGroupTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourImGroup',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImMemberData } from '@/api/generated/tourImMemberController';
+import { TourImGroupInvitationData } from '@/api/generated/tourImGroupInvitationController';
+import { TourImMessageData } from '@/api/generated/tourImMessageController';
+import { TourImGroupData } from '@/api/generated/tourImGroupController';
+import {
+  TourImMemberController,
+  TourImGroupInvitationController,
+  TourImMessageController,
+  TourImGroupController,
+  TourUserController
+} from '@/api/generated';
+import FormTourImGroupInvitation from '@/pages/ImGroup/formTourImGroupInvitation.vue';
+import FormEditTourImGroup from '@/pages/ImGroup/formEditTourImGroup.vue';
+import FormTourImMessage from '@/pages/ImGroup/formTourImMessage.vue';
+import FormTourImMember from '@/pages/ImGroup/formTourImMember.vue';
+import TourUser from "@/types/table/tourUser";
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const {
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    groupId?: ANY_OBJECT;
+  }>(),
+  {
+    subPage: 0,
+    groupId: undefined,
+  },
+);
+
+const formFilter = reactive({
+  // 群聊所属类型
+  belongTypeIdFilter: undefined,
+  // 群主
+  leaderIdFilter: undefined,
+  // 群聊名称
+  groupNameFilter: undefined,
+  // 群聊描述
+  descriptionFilter: undefined,
+  // 通知分类
+  noticeTypeFilter: undefined,
+  // 是否公开展示
+  isPublicFilter: undefined,
+  // 是否开启群聊邀请
+  isNeedConfirmFilter: undefined,
+  // 是否封禁
+  bannedStatusFilter: undefined,
+  // 热度值
+  hotValueFilter: [],
+});
+const formFilterCopy = reactive({
+  // 群聊所属类型
+  belongTypeIdFilter: undefined,
+  // 群主
+  leaderIdFilter: undefined,
+  // 群聊名称
+  groupNameFilter: undefined,
+  // 群聊描述
+  descriptionFilter: undefined,
+  // 通知分类
+  noticeTypeFilter: undefined,
+  // 是否公开展示
+  isPublicFilter: undefined,
+  // 是否开启群聊邀请
+  isNeedConfirmFilter: undefined,
+  // 是否封禁
+  bannedStatusFilter: undefined,
+  // 热度值
+  hotValueFilter: [],
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourImGroup();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourImGroupTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourImGroupDtoFilter: {
+      leaderId: formFilter.leaderIdFilter,
+      noticeType: formFilter.noticeTypeFilter,
+      groupName: formFilter.groupNameFilter,
+      description: formFilter.descriptionFilter,
+      isPublic: formFilter.isPublicFilter,
+      isNeedConfirm: formFilter.isNeedConfirmFilter,
+      bannedStatus: formFilter.bannedStatusFilter,
+      hotValueStart: Array.isArray(formFilter.hotValueFilter) ? formFilter.hotValueFilter[0] : formFilter.hotValueFilter,
+      hotValueEnd: Array.isArray(formFilter.hotValueFilter) ? formFilter.hotValueFilter[1] : formFilter.hotValueFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourImGroupController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourImGroupTableVerify = () => {
+  formFilterCopy.belongTypeIdFilter = formFilter.belongTypeIdFilter;
+  formFilterCopy.leaderIdFilter = formFilter.leaderIdFilter;
+  formFilterCopy.groupNameFilter = formFilter.groupNameFilter;
+  formFilterCopy.descriptionFilter = formFilter.descriptionFilter;
+  formFilterCopy.noticeTypeFilter = formFilter.noticeTypeFilter;
+  formFilterCopy.isPublicFilter = formFilter.isPublicFilter;
+  formFilterCopy.isNeedConfirmFilter = formFilter.isNeedConfirmFilter;
+  formFilterCopy.bannedStatusFilter = formFilter.bannedStatusFilter;
+  formFilterCopy.hotValueFilter = formFilter.hotValueFilter;
+  return true;
+};
+/**
+ * 新建
+ */
+const onAddTourImGroupClick = (row?: TourImGroupData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  Dialog
+    .show('新建', FormEditTourImGroup, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImGroupTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 聊天去聊邀请
+ */
+const onListTourImGroupInvitationClick = (row?: TourImGroupData) => {
+  let params: ANY_OBJECT = {
+    groupId: row?.id,
+  };
+
+  router.push({
+    name: 'formTourImGroupInvitation',
+    query: { ...params, subPage: true }
+  });
+};
+/**
+ * 聊天群聊成员
+ */
+const onListTourImMemberClick = (row?: TourImGroupData) => {
+  let params: ANY_OBJECT = {
+    groupId: row?.id,
+  };
+
+  router.push({
+    name: 'formTourImMember',
+    query: { ...params, subPage: true }
+  });
+};
+/**
+ * 聊天群聊消息
+ */
+const onListTourImMessageClick = (row?: TourImGroupData) => {
+  let params: ANY_OBJECT = {
+    groupId: row?.id,
+  };
+
+  router.push({
+    name: 'formTourImMessage',
+    query: { ...params, subPage: true }
+  });
+};
+/**
+ * 编辑
+ */
+const onEditTourImGroupClick = (row?: TourImGroupData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  Dialog
+    .show('编辑', FormEditTourImGroup, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImGroupTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourImGroupClick = (row?: TourImGroupData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourImGroupController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourImGroupClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourImGroupController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourImGroupTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourImGroupClick = (row?: TourImGroupData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourImGroupController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourImGroupTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourImGroupTableOptions: TableOptions<TourImGroupData> = {
+  loadTableData: loadFormTourImGroupTableWidgetData,
+  verifyTableParameter: loadFormTourImGroupTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourImGroupTable = ref();
+const formTourImGroupTableWidget = useTable(formTourImGroupTableOptions);
+const {
+  dataList: formTourImGroupTableWidgetDataList,
+  currentPage: formTourImGroupTableWidgetCurrentPage,
+  pageSize: formTourImGroupTableWidgetPageSize,
+  totalCount: formTourImGroupTableWidgetTotalCount,
+} = formTourImGroupTableWidget;
+/**
+ * 群聊所属类型下拉数据获取函数
+ */
+const loadBelongTypeIdFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroupType(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 群聊所属类型选中值改变
+ */
+const onBelongTypeIdFilterValueChange = (value) => {
+  formFilter.belongTypeIdFilter = Array.isArray(value) ? value[value.length - 1] : value;
+};
+// 群聊所属类型配置参数
+const belongTypeIdFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadBelongTypeIdFilterDropdownList,
+  isTree: true,
+};
+// 群聊所属类型选中数据
+const belongTypeIdFilterPath = ref<Array<string | number>>([]);
+// 群聊所属类型下拉组件
+const belongTypeIdFilterWidget = useDropdown(belongTypeIdFilterOptions);
+const { dropdownList: belongTypeIdFilterWidgetDropdownList } = belongTypeIdFilterWidget
+/**
+ * 群主下拉数据获取函数
+ */
+const loadLeaderIdFilterDropdownList = (): Promise<ListData<TourUser>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    TourUserController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 群主配置参数
+const leaderIdFilterOptions: DropdownOptions<TourUser> = {
+  loadData: loadLeaderIdFilterDropdownList,
+  isTree: false,
+};
+// 群主下拉组件
+const leaderIdFilterWidget = useDropdown(leaderIdFilterOptions);
+const { dropdownList: leaderIdFilterWidgetDropdownList } = leaderIdFilterWidget
+/**
+ * 通知分类下拉数据获取函数
+ */
+const loadNoticeTypeFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'TourImSingleOrGroup', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 通知分类配置参数
+const noticeTypeFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadNoticeTypeFilterDropdownList,
+  isTree: false,
+};
+// 通知分类下拉组件
+const noticeTypeFilterWidget = useDropdown(noticeTypeFilterOptions);
+const { dropdownList: noticeTypeFilterWidgetDropdownList } = noticeTypeFilterWidget
+/**
+ * 是否公开展示下拉数据获取函数
+ */
+const loadIsPublicFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 是否公开展示配置参数
+const isPublicFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadIsPublicFilterDropdownList,
+  isTree: false,
+};
+// 是否公开展示下拉组件
+const isPublicFilterWidget = useDropdown(isPublicFilterOptions);
+const { dropdownList: isPublicFilterWidgetDropdownList } = isPublicFilterWidget
+/**
+ * 是否开启群聊邀请下拉数据获取函数
+ */
+const loadIsNeedConfirmFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 是否开启群聊邀请配置参数
+const isNeedConfirmFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadIsNeedConfirmFilterDropdownList,
+  isTree: false,
+};
+// 是否开启群聊邀请下拉组件
+const isNeedConfirmFilterWidget = useDropdown(isNeedConfirmFilterOptions);
+const { dropdownList: isNeedConfirmFilterWidgetDropdownList } = isNeedConfirmFilterWidget
+/**
+ * 是否封禁 下拉数据获取函数
+ */
+const loadBannedStatusFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 是否封禁 配置参数
+const bannedStatusFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadBannedStatusFilterDropdownList,
+  isTree: false,
+};
+// 是否封禁 下拉组件
+const bannedStatusFilterWidget = useDropdown(bannedStatusFilterOptions);
+const { dropdownList: bannedStatusFilterWidgetDropdownList } = bannedStatusFilterWidget
+const refreshFormTourImGroup = () => {
+  // 刷新段落
+  formTourImGroupTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourImGroup = () => {
+  formFilter.belongTypeIdFilter = undefined;
+  formFilterCopy.belongTypeIdFilter = undefined;
+  belongTypeIdFilterPath.value = [];
+  formFilter.leaderIdFilter = undefined;
+  formFilterCopy.leaderIdFilter = undefined;
+  formFilter.groupNameFilter = undefined;
+  formFilterCopy.groupNameFilter = undefined;
+  formFilter.descriptionFilter = undefined;
+  formFilterCopy.descriptionFilter = undefined;
+  formFilter.noticeTypeFilter = undefined;
+  formFilterCopy.noticeTypeFilter = undefined;
+  formFilter.isPublicFilter = undefined;
+  formFilterCopy.isPublicFilter = undefined;
+  formFilter.isNeedConfirmFilter = undefined;
+  formFilterCopy.isNeedConfirmFilter = undefined;
+  formFilter.bannedStatusFilter = undefined;
+  formFilterCopy.bannedStatusFilter = undefined;
+  formFilter.hotValueFilter = undefined;
+  formFilterCopy.hotValueFilter = undefined;
+  refreshFormTourImGroup();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourImGroup();
+};
+const formInit = () => {
+  belongTypeIdFilterWidget.onVisibleChange(true).then(res => {
+    // TODO: 获取级联选中路径
+    belongTypeIdFilterPath.value = findTreeNodePath(res, formFilter.belongTypeIdFilter);
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+  leaderIdFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  noticeTypeFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  isPublicFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  isNeedConfirmFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  bannedStatusFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  refreshFormTourImGroup();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script>

+ 528 - 0
src/pages/ImGroup/formTourImGroupInvitation.vue

@@ -0,0 +1,528 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourImGroupInvitationRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourImGroupInvitation()" @reset="resetFormTourImGroupInvitation">
+        <el-form-item label="聊天群组">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.groupIdFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="groupIdFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in groupIdFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="邀请人">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.inviterFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="inviterFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in inviterFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="被邀请人">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.inviteeFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="inviteeFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in inviteeFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="邀请状态 ">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.statusFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="statusFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in statusFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourImGroupInvitationTable"
+      class="page-table"
+      :data="formTourImGroupInvitationTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourImGroupInvitationTableWidgetCurrentPage - 1) * formTourImGroupInvitationTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourImGroupInvitationTableWidget.onSortChange"
+      @refresh="formTourImGroupInvitationTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          @click="onExportTourImGroupInvitationClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :on-change="onImportTourImGroupInvitationClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourImGroupInvitationTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="聊天群组" field="groupIdDictMap.name" />
+      <vxe-column title="邀请人" field="inviterDictMap.name" />
+      <vxe-column title="被邀请人" field="inviteeDictMap.name" />
+      <vxe-column title="邀请状态" field="statusDictMap.name" />
+      <vxe-column title="创建人" field="createUserId" />
+      <vxe-column title="创建时间" field="createTime">
+        <template v-slot="scope">
+          <span>{{formatDateByStatsType(scope.row.createTime, 'day')}}</span>
+        </template>
+      </vxe-column>
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onEditTourImGroupInvitationClick(scope.row)"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onDeleteTourImGroupInvitationClick(scope.row)"
+          >
+            删除
+          </el-button>
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourImGroupInvitationTableWidgetTotalCount"
+            :current-page="formTourImGroupInvitationTableWidgetCurrentPage"
+            :page-size="formTourImGroupInvitationTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourImGroupInvitationTableWidget.onCurrentPageChange"
+            @size-change="formTourImGroupInvitationTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourImGroupInvitation',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImGroupInvitationData } from '@/api/generated/tourImGroupInvitationController';
+import { TourImGroupInvitationController } from '@/api/generated';
+import FormEditTourImGroupInvitation from '@/pages/ImGroup/formEditTourImGroupInvitation.vue';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    groupId?: ANY_OBJECT;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    groupId: undefined,
+  },
+);
+
+const formFilter = reactive({
+  // 聊天群组
+  groupIdFilter: undefined,
+  // 邀请人
+  inviterFilter: undefined,
+  // 被邀请人
+  inviteeFilter: undefined,
+  // 邀请状态 
+  statusFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 聊天群组
+  groupIdFilter: undefined,
+  // 邀请人
+  inviterFilter: undefined,
+  // 被邀请人
+  inviteeFilter: undefined,
+  // 邀请状态 
+  statusFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourImGroupInvitation();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourImGroupInvitationTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourImGroupInvitationDtoFilter: {
+      groupId: props.groupId,
+      inviter: formFilter.inviterFilter,
+      invitee: formFilter.inviteeFilter,
+      status: formFilter.statusFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourImGroupInvitationController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourImGroupInvitationTableVerify = () => {
+  formFilterCopy.groupIdFilter = formFilter.groupIdFilter;
+  formFilterCopy.inviterFilter = formFilter.inviterFilter;
+  formFilterCopy.inviteeFilter = formFilter.inviteeFilter;
+  formFilterCopy.statusFilter = formFilter.statusFilter;
+  return true;
+};
+/**
+ * 编辑
+ */
+const onEditTourImGroupInvitationClick = (row?: TourImGroupInvitationData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  Dialog
+    .show('编辑', FormEditTourImGroupInvitation, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImGroupInvitationTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourImGroupInvitationClick = (row?: TourImGroupInvitationData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourImGroupInvitationController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourImGroupInvitationClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourImGroupInvitationController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourImGroupInvitationTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourImGroupInvitationClick = (row?: TourImGroupInvitationData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourImGroupInvitationController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourImGroupInvitationTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourImGroupInvitationTableOptions: TableOptions<TourImGroupInvitationData> = {
+  loadTableData: loadFormTourImGroupInvitationTableWidgetData,
+  verifyTableParameter: loadFormTourImGroupInvitationTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourImGroupInvitationTable = ref();
+const formTourImGroupInvitationTableWidget = useTable(formTourImGroupInvitationTableOptions);
+const {
+  dataList: formTourImGroupInvitationTableWidgetDataList,
+  currentPage: formTourImGroupInvitationTableWidgetCurrentPage,
+  pageSize: formTourImGroupInvitationTableWidgetPageSize,
+  totalCount: formTourImGroupInvitationTableWidgetTotalCount,
+} = formTourImGroupInvitationTableWidget;
+/**
+ * 聊天群组下拉数据获取函数
+ */
+const loadGroupIdFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroup(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 聊天群组配置参数
+const groupIdFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadGroupIdFilterDropdownList,
+  isTree: false,
+};
+// 聊天群组下拉组件
+const groupIdFilterWidget = useDropdown(groupIdFilterOptions);
+const { dropdownList: groupIdFilterWidgetDropdownList } = groupIdFilterWidget
+/**
+ * 邀请人下拉数据获取函数
+ */
+const loadInviterFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourUser(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 邀请人配置参数
+const inviterFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadInviterFilterDropdownList,
+  isTree: false,
+};
+// 邀请人下拉组件
+const inviterFilterWidget = useDropdown(inviterFilterOptions);
+const { dropdownList: inviterFilterWidgetDropdownList } = inviterFilterWidget
+/**
+ * 被邀请人下拉数据获取函数
+ */
+const loadInviteeFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourUser(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 被邀请人配置参数
+const inviteeFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadInviteeFilterDropdownList,
+  isTree: false,
+};
+// 被邀请人下拉组件
+const inviteeFilterWidget = useDropdown(inviteeFilterOptions);
+const { dropdownList: inviteeFilterWidgetDropdownList } = inviteeFilterWidget
+/**
+ * 邀请状态 下拉数据获取函数
+ */
+const loadStatusFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'GroupInvitationStatus', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 邀请状态 配置参数
+const statusFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadStatusFilterDropdownList,
+  isTree: false,
+};
+// 邀请状态 下拉组件
+const statusFilterWidget = useDropdown(statusFilterOptions);
+const { dropdownList: statusFilterWidgetDropdownList } = statusFilterWidget
+const refreshFormTourImGroupInvitation = () => {
+  // 刷新段落
+  formTourImGroupInvitationTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourImGroupInvitation = () => {
+  formFilter.groupIdFilter = undefined;
+  formFilterCopy.groupIdFilter = undefined;
+  formFilter.inviterFilter = undefined;
+  formFilterCopy.inviterFilter = undefined;
+  formFilter.inviteeFilter = undefined;
+  formFilterCopy.inviteeFilter = undefined;
+  formFilter.statusFilter = undefined;
+  formFilterCopy.statusFilter = undefined;
+  refreshFormTourImGroupInvitation();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourImGroupInvitation();
+};
+const formInit = () => {
+  groupIdFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  inviterFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  inviteeFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  statusFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  refreshFormTourImGroupInvitation();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script>

+ 558 - 0
src/pages/ImGroup/formTourImGroupType.vue

@@ -0,0 +1,558 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourImGroupTypeRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourImGroupType()" @reset="resetFormTourImGroupType">
+<!--        <el-form-item label="类型选择">-->
+<!--          <el-cascader-->
+<!--            class="filter-item"-->
+<!--            v-model="idFilterPath"-->
+<!--            :options="idFilterWidgetDropdownList"-->
+<!--            placeholder=""-->
+<!--            :clearable="true"-->
+<!--            :filterable="true"-->
+<!--            :show-all-levels="false"-->
+<!--            :props="{ value: 'id', label: 'name', children: 'children', checkStrictly: true }"-->
+<!--            @change="onIdFilterValueChange"-->
+<!--          />-->
+<!--        </el-form-item>-->
+       <!-- <el-form-item label="一级类型选择">
+          <el-cascader
+            class="filter-item"
+            v-model="parentIdFilter_copyPath"
+            :options="parentIdFilter_copyWidgetDropdownList"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            :show-all-levels="false"
+            :props="{ value: 'id', label: 'name', children: 'children', checkStrictly: true }"
+            @change="onParentIdFilter_copyValueChange"
+          />
+        </el-form-item> -->
+        <el-form-item label="类型名称">
+          <el-input
+            class="filter-item"
+            v-model="formFilter.typeNameFilter"
+            type="text"
+            placeholder=""
+            :clearable="true"
+            :show-word-limit="false"
+            maxlength=""
+          />
+        </el-form-item>
+        <el-form-item label="是否启用 ">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.enableFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="enableFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in enableFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourImGroupTypeTable"
+      class="page-table"
+      :data="formTourImGroupTypeTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourImGroupTypeTableWidgetCurrentPage - 1) * formTourImGroupTypeTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourImGroupTypeTableWidget.onSortChange"
+      @refresh="formTourImGroupTypeTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImGroupType:formTourImGroupType:addTourImGroupType')"
+          @click="onAddTourImGroupTypeClick()"
+          >
+          新建
+        </el-button>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImGroupType:formTourImGroupType:exportTourImGroupType')"
+          @click="onExportTourImGroupTypeClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :disabled="!checkPermCodeExist('formTourImGroupType:formTourImGroupType:importTourImGroupType')"
+          :on-change="onImportTourImGroupTypeClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+              :disabled="!checkPermCodeExist('formTourImGroupType:formTourImGroupType:importTourImGroupType')"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourImGroupTypeTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="父级类型" field="parentIdDictMap.name" >
+        <template v-slot="scope">
+          <span>{{scope.row?.parentIdDictMap?.name ?? '-'}}</span>
+        </template>
+      </vxe-column>>
+
+      <vxe-column title="类型名称" field="typeName" />
+      <vxe-column title="一级类型图标">
+        <template v-slot="scope">
+          <upload-file-list
+            :file-list="
+              parseUploadData(scope.row.typeIcon, {
+                id: scope.row.id,
+                fieldName: 'typeIcon',
+                asImage: true
+              })
+            "
+            type="card"
+            direction="horizontal"
+            :readonly="true"
+          />
+        </template>
+      </vxe-column>
+      <vxe-column title="是否启用 " field="enableDictMap.name" />
+<!--      <vxe-column title="创建人id" field="createUserId" />-->
+      <vxe-column title="创建时间" field="createTime">
+        <template v-slot="scope">
+          <span>{{formatDateByStatsType(scope.row.createTime, 'day')}}</span>
+        </template>
+      </vxe-column>
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onEditTourImGroupTypeClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImGroupType:formTourImGroupType:editTourImGroupType')"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onDeleteTourImGroupTypeClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImGroupType:formTourImGroupType:deleteTourImGroupType')"
+          >
+            删除
+          </el-button>
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourImGroupTypeTableWidgetTotalCount"
+            :current-page="formTourImGroupTypeTableWidgetCurrentPage"
+            :page-size="formTourImGroupTypeTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourImGroupTypeTableWidget.onCurrentPageChange"
+            @size-change="formTourImGroupTypeTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourImGroupType',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImGroupTypeData } from '@/api/generated/tourImGroupTypeController';
+import { TourImGroupTypeController } from '@/api/generated';
+import FormEditTourImGroupType from '@/pages/ImGroup/formEditTourImGroupType.vue';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const {
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+  }>(),
+  {
+    subPage: 0,
+  },
+);
+
+const formFilter = reactive({
+  // 类型选择
+  idFilter: undefined,
+  // 一级类型选择
+  parentIdFilter_copy: undefined,
+  // 类型名称
+  typeNameFilter: undefined,
+  // 是否启用
+  enableFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 类型选择
+  idFilter: undefined,
+  // 一级类型选择
+  parentIdFilter_copy: undefined,
+  // 类型名称
+  typeNameFilter: undefined,
+  // 是否启用
+  enableFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourImGroupType();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourImGroupTypeTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourImGroupTypeDtoFilter: {
+      id: formFilter.idFilter,
+      parentId: formFilter.parentIdFilter_copy,
+      typeName: formFilter.typeNameFilter,
+      enable: formFilter.enableFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourImGroupTypeController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourImGroupTypeTableVerify = () => {
+  formFilterCopy.idFilter = formFilter.idFilter;
+  formFilterCopy.parentIdFilter_copy = formFilter.parentIdFilter_copy;
+  formFilterCopy.typeNameFilter = formFilter.typeNameFilter;
+  formFilterCopy.enableFilter = formFilter.enableFilter;
+  return true;
+};
+/**
+ * 新建
+ */
+const onAddTourImGroupTypeClick = (row?: TourImGroupTypeData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  Dialog
+    .show('新建', FormEditTourImGroupType, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImGroupTypeTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 编辑
+ */
+const onEditTourImGroupTypeClick = (row?: TourImGroupTypeData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  Dialog
+    .show('编辑', FormEditTourImGroupType, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImGroupTypeTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourImGroupTypeClick = (row?: TourImGroupTypeData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourImGroupTypeController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourImGroupTypeClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourImGroupTypeController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourImGroupTypeTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourImGroupTypeClick = (row?: TourImGroupTypeData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourImGroupTypeController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourImGroupTypeTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourImGroupTypeTableOptions: TableOptions<TourImGroupTypeData> = {
+  loadTableData: loadFormTourImGroupTypeTableWidgetData,
+  verifyTableParameter: loadFormTourImGroupTypeTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourImGroupTypeTable = ref();
+const formTourImGroupTypeTableWidget = useTable(formTourImGroupTypeTableOptions);
+const {
+  dataList: formTourImGroupTypeTableWidgetDataList,
+  currentPage: formTourImGroupTypeTableWidgetCurrentPage,
+  pageSize: formTourImGroupTypeTableWidgetPageSize,
+  totalCount: formTourImGroupTypeTableWidgetTotalCount,
+} = formTourImGroupTypeTableWidget;
+/**
+ * 类型选择下拉数据获取函数
+ */
+const loadIdFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroupType(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 类型选择选中值改变
+ */
+const onIdFilterValueChange = (value) => {
+  formFilter.idFilter = Array.isArray(value) ? value[value.length - 1] : value;
+};
+// 类型选择配置参数
+const idFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadIdFilterDropdownList,
+  isTree: true,
+};
+// 类型选择选中数据
+const idFilterPath = ref<Array<string | number>>([]);
+// 类型选择下拉组件
+const idFilterWidget = useDropdown(idFilterOptions);
+const { dropdownList: idFilterWidgetDropdownList } = idFilterWidget
+/**
+ * 一级类型选择下拉数据获取函数
+ */
+const loadParentIdFilter_copyDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroupType(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 一级类型选择选中值改变
+ */
+const onParentIdFilter_copyValueChange = (value) => {
+  formFilter.parentIdFilter_copy = Array.isArray(value) ? value[value.length - 1] : value;
+};
+// 一级类型选择配置参数
+const parentIdFilter_copyOptions: DropdownOptions<DictData> = {
+  loadData: loadParentIdFilter_copyDropdownList,
+  isTree: true,
+};
+// 一级类型选择选中数据
+const parentIdFilter_copyPath = ref<Array<string | number>>([]);
+// 一级类型选择下拉组件
+const parentIdFilter_copyWidget = useDropdown(parentIdFilter_copyOptions);
+const { dropdownList: parentIdFilter_copyWidgetDropdownList } = parentIdFilter_copyWidget
+/**
+ * 是否启用 下拉数据获取函数
+ */
+const loadEnableFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.Enable.getList(),
+  });
+};
+// 是否启用 配置参数
+const enableFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadEnableFilterDropdownList,
+  isTree: false,
+};
+// 是否启用 下拉组件
+const enableFilterWidget = useDropdown(enableFilterOptions);
+const { dropdownList: enableFilterWidgetDropdownList } = enableFilterWidget
+const refreshFormTourImGroupType = () => {
+  // 刷新段落
+  formTourImGroupTypeTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourImGroupType = () => {
+  formFilter.idFilter = undefined;
+  formFilterCopy.idFilter = undefined;
+  idFilterPath.value = [];
+  formFilter.parentIdFilter_copy = undefined;
+  formFilterCopy.parentIdFilter_copy = undefined;
+  parentIdFilter_copyPath.value = [];
+  formFilter.typeNameFilter = undefined;
+  formFilterCopy.typeNameFilter = undefined;
+  formFilter.enableFilter = undefined;
+  formFilterCopy.enableFilter = undefined;
+  refreshFormTourImGroupType();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourImGroupType();
+};
+const formInit = () => {
+  idFilterWidget.onVisibleChange(true).then(res => {
+    // TODO: 获取级联选中路径
+    idFilterPath.value = findTreeNodePath(res, formFilter.idFilter);
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+  parentIdFilter_copyWidget.onVisibleChange(true).then(res => {
+    // TODO: 获取级联选中路径
+    parentIdFilter_copyPath.value = findTreeNodePath(res, formFilter.parentIdFilter_copy);
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+  enableFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  refreshFormTourImGroupType();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script>

+ 595 - 0
src/pages/ImGroup/formTourImMember.vue

@@ -0,0 +1,595 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourImMemberRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourImMember()" @reset="resetFormTourImMember">
+        <el-form-item label="群名称">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.groupIdFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="groupIdFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in groupIdFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="成员名称">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.userIdFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="userIdFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in userIdFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="成员角色">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.groupRoleFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="groupRoleFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in groupRoleFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="会话是否显示">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.isShowFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="isShowFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in isShowFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="会话是否置顶 ">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.isTopFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="isTopFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in isTopFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourImMemberTable"
+      class="page-table"
+      :data="formTourImMemberTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourImMemberTableWidgetCurrentPage - 1) * formTourImMemberTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourImMemberTableWidget.onSortChange"
+      @refresh="formTourImMemberTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          @click="onExportTourImMemberClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :on-change="onImportTourImMemberClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourImMemberTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="聊天群组" field="groupIdDictMap.name" />
+      <vxe-column title="成员角色" field="groupRoleDictMap.name" />
+      <vxe-column title="用户" field="userIdDictMap.name" />
+      <vxe-column title="是否显示" field="isShowDictMap.name" />
+      <vxe-column title="是否置顶 " field="isTopDictMap.name" />
+      <vxe-column title="是免打扰 " field="isNotDisturbDictMap.name" />
+      <vxe-column title="群昵称" field="groupNickname" />
+      <vxe-column title="群备注" field="groupRemark" />
+
+      <vxe-column title="聊天背景">
+        <template v-slot="scope">
+          <upload-file-list
+              :file-list="
+              parseUploadData(scope.row.groupBackImage, {
+                id: scope.row.id,
+                fieldName: 'groupBackImage',
+                asImage: true
+              })
+            "
+              type="card"
+              direction="horizontal"
+              :readonly="true"
+          />
+        </template>
+      </vxe-column>
+
+<!--      <vxe-column title="聊天背景">-->
+<!--        <template v-slot="scope">-->
+<!--          <upload-file-list-->
+<!--            :file-list="-->
+<!--              parseUploadData(scope.row.groupBackImage, {-->
+<!--                id: scope.row.id,-->
+<!--                fieldName: 'groupBackImage',-->
+<!--                asImage: true-->
+<!--              })-->
+<!--            "-->
+<!--            type="card"-->
+<!--            direction="horizontal"-->
+<!--            :readonly="true"-->
+<!--          />-->
+<!--        </template>-->
+<!--      </vxe-column>-->
+<!--      <vxe-column title="操作" fixed="right">-->
+<!--        <template v-slot="scope">-->
+<!--          <el-button-->
+<!--            link-->
+<!--            type="primary"-->
+<!--            :size="layoutStore.defaultFormItemSize"-->
+<!--            @click.stop="onEditTourImMemberClick(scope.row)"-->
+<!--          >-->
+<!--            编辑-->
+<!--          </el-button>-->
+<!--          <el-button-->
+<!--            link-->
+<!--            type="primary"-->
+<!--            :size="layoutStore.defaultFormItemSize"-->
+<!--            @click.stop="onDeleteTourImMemberClick(scope.row)"-->
+<!--          >-->
+<!--            删除-->
+<!--          </el-button>-->
+<!--        </template>-->
+<!--      </vxe-column>-->
+      <vxe-column title="进群时间" field="createTime" />
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourImMemberTableWidgetTotalCount"
+            :current-page="formTourImMemberTableWidgetCurrentPage"
+            :page-size="formTourImMemberTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourImMemberTableWidget.onCurrentPageChange"
+            @size-change="formTourImMemberTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourImMember',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImMemberData } from '@/api/generated/tourImMemberController';
+import { TourImMemberController } from '@/api/generated';
+import FormEditTourImMember from '@/pages/ImGroup/formEditTourImMember.vue';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const {
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    groupId?: ANY_OBJECT;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    groupId: undefined,
+  },
+);
+
+const formFilter = reactive({
+  // 群名称
+  groupIdFilter: undefined,
+  // 成员名称
+  userIdFilter: undefined,
+  // 成员角色
+  groupRoleFilter: undefined,
+  // 会话是否显示
+  isShowFilter: undefined,
+  // 会话是否置顶
+  isTopFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 群名称
+  groupIdFilter: undefined,
+  // 成员名称
+  userIdFilter: undefined,
+  // 成员角色
+  groupRoleFilter: undefined,
+  // 会话是否显示
+  isShowFilter: undefined,
+  // 会话是否置顶
+  isTopFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourImMember();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourImMemberTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourImMemberDtoFilter: {
+      groupId: props.groupId,
+      groupRole: formFilter.groupRoleFilter,
+      userId: formFilter.userIdFilter,
+      isShow: formFilter.isShowFilter,
+      isTop: formFilter.isTopFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourImMemberController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourImMemberTableVerify = () => {
+  formFilterCopy.groupIdFilter = formFilter.groupIdFilter;
+  formFilterCopy.userIdFilter = formFilter.userIdFilter;
+  formFilterCopy.groupRoleFilter = formFilter.groupRoleFilter;
+  formFilterCopy.isShowFilter = formFilter.isShowFilter;
+  formFilterCopy.isTopFilter = formFilter.isTopFilter;
+  return true;
+};
+/**
+ * 编辑
+ */
+const onEditTourImMemberClick = (row?: TourImMemberData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  Dialog
+    .show('编辑', FormEditTourImMember, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImMemberTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourImMemberClick = (row?: TourImMemberData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourImMemberController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourImMemberClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourImMemberController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourImMemberTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourImMemberClick = (row?: TourImMemberData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourImMemberController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourImMemberTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourImMemberTableOptions: TableOptions<TourImMemberData> = {
+  loadTableData: loadFormTourImMemberTableWidgetData,
+  verifyTableParameter: loadFormTourImMemberTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourImMemberTable = ref();
+const formTourImMemberTableWidget = useTable(formTourImMemberTableOptions);
+const {
+  dataList: formTourImMemberTableWidgetDataList,
+  currentPage: formTourImMemberTableWidgetCurrentPage,
+  pageSize: formTourImMemberTableWidgetPageSize,
+  totalCount: formTourImMemberTableWidgetTotalCount,
+} = formTourImMemberTableWidget;
+/**
+ * 群名称下拉数据获取函数
+ */
+const loadGroupIdFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroup(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 群名称配置参数
+const groupIdFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadGroupIdFilterDropdownList,
+  isTree: false,
+};
+// 群名称下拉组件
+const groupIdFilterWidget = useDropdown(groupIdFilterOptions);
+const { dropdownList: groupIdFilterWidgetDropdownList } = groupIdFilterWidget
+/**
+ * 成员名称下拉数据获取函数
+ */
+const loadUserIdFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictSysUser(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 成员名称配置参数
+const userIdFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadUserIdFilterDropdownList,
+  isTree: false,
+};
+// 成员名称下拉组件
+const userIdFilterWidget = useDropdown(userIdFilterOptions);
+const { dropdownList: userIdFilterWidgetDropdownList } = userIdFilterWidget
+/**
+ * 成员角色下拉数据获取函数
+ */
+const loadGroupRoleFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'GroupRole', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 成员角色配置参数
+const groupRoleFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadGroupRoleFilterDropdownList,
+  isTree: false,
+};
+// 成员角色下拉组件
+const groupRoleFilterWidget = useDropdown(groupRoleFilterOptions);
+const { dropdownList: groupRoleFilterWidgetDropdownList } = groupRoleFilterWidget
+/**
+ * 会话是否显示下拉数据获取函数
+ */
+const loadIsShowFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 会话是否显示配置参数
+const isShowFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadIsShowFilterDropdownList,
+  isTree: false,
+};
+// 会话是否显示下拉组件
+const isShowFilterWidget = useDropdown(isShowFilterOptions);
+const { dropdownList: isShowFilterWidgetDropdownList } = isShowFilterWidget
+/**
+ * 会话是否置顶 下拉数据获取函数
+ */
+const loadIsTopFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 会话是否置顶 配置参数
+const isTopFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadIsTopFilterDropdownList,
+  isTree: false,
+};
+// 会话是否置顶 下拉组件
+const isTopFilterWidget = useDropdown(isTopFilterOptions);
+const { dropdownList: isTopFilterWidgetDropdownList } = isTopFilterWidget
+const refreshFormTourImMember = () => {
+  // 刷新段落
+  formTourImMemberTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourImMember = () => {
+  formFilter.groupIdFilter = undefined;
+  formFilterCopy.groupIdFilter = undefined;
+  formFilter.userIdFilter = undefined;
+  formFilterCopy.userIdFilter = undefined;
+  formFilter.groupRoleFilter = undefined;
+  formFilterCopy.groupRoleFilter = undefined;
+  formFilter.isShowFilter = undefined;
+  formFilterCopy.isShowFilter = undefined;
+  formFilter.isTopFilter = undefined;
+  formFilterCopy.isTopFilter = undefined;
+  refreshFormTourImMember();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourImMember();
+};
+const formInit = () => {
+  groupIdFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  userIdFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  groupRoleFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  isShowFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  isTopFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  refreshFormTourImMember();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script>

+ 559 - 0
src/pages/ImGroup/formTourImMessage.vue

@@ -0,0 +1,559 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourImMessageRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourImMessage()" @reset="resetFormTourImMessage">
+        <el-form-item label="消息内容">
+          <el-input
+            class="filter-item"
+            v-model="formFilter.messageContentFilter"
+            type="text"
+            placeholder=""
+            :clearable="true"
+            :show-word-limit="false"
+            maxlength=""
+          />
+        </el-form-item>
+        <el-form-item label="消息类型">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.messageTypeFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="messageTypeFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in messageTypeFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="聊天群组">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.groupIdFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="groupIdFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in groupIdFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="消息置顶/公告">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.isTopFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="isTopFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in isTopFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="撤回标记 ">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.revocationTagFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="revocationTagFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in revocationTagFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourImMessageTable"
+      class="page-table"
+      :data="formTourImMessageTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourImMessageTableWidgetCurrentPage - 1) * formTourImMessageTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourImMessageTableWidget.onSortChange"
+      @refresh="formTourImMessageTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          @click="onAddTourImMessageClick()"
+          >
+          新建
+        </el-button>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          @click="onExportTourImMessageClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :on-change="onImportTourImMessageClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourImMessageTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="消息内容" field="messageContent" />
+      <vxe-column title="消息类型" field="messageTypeDictMap.name" />
+      <vxe-column title="聊天群组" field="groupIdDictMap.name" />
+      <vxe-column title="消息置顶/公告" field="isTopDictMap.name" />
+      <vxe-column title="撤回标记 " field="revocationTagDictMap.name" />
+      <vxe-column title="已经删除该条消息的用户" field="deletedByUsers" />
+      <vxe-column title="已读该条消息的用户" field="readByUsers" />
+      <vxe-column title="删除标记" field="dataState" />
+      <vxe-column title="创建人" field="createUserIdDictMap.name" />
+      <vxe-column title="创建时间" field="createTime">
+        <template v-slot="scope">
+          <span>{{formatDateByStatsType(scope.row.createTime, 'day')}}</span>
+        </template>
+      </vxe-column>
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onEditTourImMessageClick(scope.row)"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onDeleteTourImMessageClick(scope.row)"
+          >
+            删除
+          </el-button>
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourImMessageTableWidgetTotalCount"
+            :current-page="formTourImMessageTableWidgetCurrentPage"
+            :page-size="formTourImMessageTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourImMessageTableWidget.onCurrentPageChange"
+            @size-change="formTourImMessageTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourImMessage',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImMessageData } from '@/api/generated/tourImMessageController';
+import { TourImMessageController } from '@/api/generated';
+import FormEditTourImMessage from '@/pages/ImGroup/formEditTourImMessage.vue';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    groupId?: ANY_OBJECT;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    groupId: undefined,
+  },
+);
+
+const formFilter = reactive({
+  // 消息内容
+  messageContentFilter: undefined,
+  // 消息类型
+  messageTypeFilter: undefined,
+  // 聊天群组
+  groupIdFilter: undefined,
+  // 消息置顶/公告
+  isTopFilter: undefined,
+  // 撤回标记 
+  revocationTagFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 消息内容
+  messageContentFilter: undefined,
+  // 消息类型
+  messageTypeFilter: undefined,
+  // 聊天群组
+  groupIdFilter: undefined,
+  // 消息置顶/公告
+  isTopFilter: undefined,
+  // 撤回标记 
+  revocationTagFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourImMessage();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourImMessageTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourImMessageDtoFilter: {
+      messageContent: formFilter.messageContentFilter,
+      messageType: formFilter.messageTypeFilter,
+      groupId: props.groupId,
+      noticeType: formFilter.messageTypeFilter,
+      isTop: formFilter.isTopFilter,
+      revocationTag: formFilter.revocationTagFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourImMessageController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourImMessageTableVerify = () => {
+  formFilterCopy.messageContentFilter = formFilter.messageContentFilter;
+  formFilterCopy.messageTypeFilter = formFilter.messageTypeFilter;
+  formFilterCopy.groupIdFilter = formFilter.groupIdFilter;
+  formFilterCopy.isTopFilter = formFilter.isTopFilter;
+  formFilterCopy.revocationTagFilter = formFilter.revocationTagFilter;
+  return true;
+};
+/**
+ * 新建
+ */
+const onAddTourImMessageClick = (row?: TourImMessageData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  Dialog
+    .show('新建', FormEditTourImMessage, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImMessageTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 编辑
+ */
+const onEditTourImMessageClick = (row?: TourImMessageData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  Dialog
+    .show('编辑', FormEditTourImMessage, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImMessageTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourImMessageClick = (row?: TourImMessageData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourImMessageController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourImMessageClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourImMessageController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourImMessageTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourImMessageClick = (row?: TourImMessageData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourImMessageController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourImMessageTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourImMessageTableOptions: TableOptions<TourImMessageData> = {
+  loadTableData: loadFormTourImMessageTableWidgetData,
+  verifyTableParameter: loadFormTourImMessageTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourImMessageTable = ref();
+const formTourImMessageTableWidget = useTable(formTourImMessageTableOptions);
+const {
+  dataList: formTourImMessageTableWidgetDataList,
+  currentPage: formTourImMessageTableWidgetCurrentPage,
+  pageSize: formTourImMessageTableWidgetPageSize,
+  totalCount: formTourImMessageTableWidgetTotalCount,
+} = formTourImMessageTableWidget;
+/**
+ * 消息类型下拉数据获取函数
+ */
+const loadMessageTypeFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'MessageType', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 消息类型配置参数
+const messageTypeFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadMessageTypeFilterDropdownList,
+  isTree: false,
+};
+// 消息类型下拉组件
+const messageTypeFilterWidget = useDropdown(messageTypeFilterOptions);
+const { dropdownList: messageTypeFilterWidgetDropdownList } = messageTypeFilterWidget
+/**
+ * 聊天群组下拉数据获取函数
+ */
+const loadGroupIdFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    DictionaryController.dictTourImGroup(params).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 聊天群组配置参数
+const groupIdFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadGroupIdFilterDropdownList,
+  isTree: false,
+};
+// 聊天群组下拉组件
+const groupIdFilterWidget = useDropdown(groupIdFilterOptions);
+const { dropdownList: groupIdFilterWidgetDropdownList } = groupIdFilterWidget
+/**
+ * 消息置顶/公告下拉数据获取函数
+ */
+const loadIsTopFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 消息置顶/公告配置参数
+const isTopFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadIsTopFilterDropdownList,
+  isTree: false,
+};
+// 消息置顶/公告下拉组件
+const isTopFilterWidget = useDropdown(isTopFilterOptions);
+const { dropdownList: isTopFilterWidgetDropdownList } = isTopFilterWidget
+/**
+ * 撤回标记 下拉数据获取函数
+ */
+const loadRevocationTagFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return Promise.resolve({
+    dataList: StaticDict.IsTrue.getList(),
+  });
+};
+// 撤回标记 配置参数
+const revocationTagFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadRevocationTagFilterDropdownList,
+  isTree: false,
+};
+// 撤回标记 下拉组件
+const revocationTagFilterWidget = useDropdown(revocationTagFilterOptions);
+const { dropdownList: revocationTagFilterWidgetDropdownList } = revocationTagFilterWidget
+const refreshFormTourImMessage = () => {
+  // 刷新段落
+  formTourImMessageTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourImMessage = () => {
+  formFilter.messageContentFilter = undefined;
+  formFilterCopy.messageContentFilter = undefined;
+  formFilter.messageTypeFilter = undefined;
+  formFilterCopy.messageTypeFilter = undefined;
+  formFilter.groupIdFilter = undefined;
+  formFilterCopy.groupIdFilter = undefined;
+  formFilter.isTopFilter = undefined;
+  formFilterCopy.isTopFilter = undefined;
+  formFilter.revocationTagFilter = undefined;
+  formFilterCopy.revocationTagFilter = undefined;
+  refreshFormTourImMessage();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourImMessage();
+};
+const formInit = () => {
+  messageTypeFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  groupIdFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  isTopFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  revocationTagFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  refreshFormTourImMessage();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script>

+ 225 - 0
src/pages/SensitiveWord/formEditTourImSensitiveWordAllow.vue

@@ -0,0 +1,225 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourImSensitiveWordAllowRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="白名单词汇" prop="TourImSensitiveWordAllow.word">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImSensitiveWordAllow.word"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourImSensitiveWordAllowClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourImSensitiveWordAllow',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImSensitiveWordAllowData } from '@/api/generated/tourImSensitiveWordAllowController';
+import { TourImSensitiveWordAllowController } from '@/api/generated';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourImSensitiveWordAllowRef = ref();
+// 表单数据定义
+type FormEditTourImSensitiveWordAllowData = {
+  TourImSensitiveWordAllow: TourImSensitiveWordAllowData;
+};
+// 表单数据
+const formData = reactive<FormEditTourImSensitiveWordAllowData>({
+  TourImSensitiveWordAllow: {
+    // id
+    id: undefined,
+    // 白名单词汇
+    word: undefined,
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourImSensitiveWordAllow.word': [
+    {required: true, message: '请输入白名单词汇', trigger: 'blur'}
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourImSensitiveWordAllowData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourImSensitiveWordAllow = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourImSensitiveWordAllowController.view(params).then(res => {
+      formData.TourImSensitiveWordAllow = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourImSensitiveWordAllow = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourImSensitiveWordAllow = () => {
+  refreshFormEditTourImSensitiveWordAllow();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourImSensitiveWordAllow();
+};
+/**
+ * 保存
+ */
+const onSubmitTourImSensitiveWordAllowClick = () => {
+  formEditTourImSensitiveWordAllowRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourImSensitiveWordAllow
+      };
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourImSensitiveWordAllowDto: {
+        id: formData.TourImSensitiveWordAllow.id,
+        word: formData.TourImSensitiveWordAllow.word,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourImSensitiveWordAllowController.update : TourImSensitiveWordAllowController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourImSensitiveWordAllowData().then(res => {
+    if (isEdit.value) refreshFormEditTourImSensitiveWordAllow();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 225 - 0
src/pages/SensitiveWord/formEditTourImSensitiveWordDeny.vue

@@ -0,0 +1,225 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourImSensitiveWordDenyRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="黑名单词汇" prop="TourImSensitiveWordDeny.word">
+              <el-input
+                class="input-item"
+                v-model="formData.TourImSensitiveWordDeny.word"
+                type="text"
+                placeholder=""
+                :clearable="true"
+                :show-word-limit="false"
+                maxlength=""
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourImSensitiveWordDenyClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourImSensitiveWordDeny',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImSensitiveWordDenyData } from '@/api/generated/tourImSensitiveWordDenyController';
+import { TourImSensitiveWordDenyController } from '@/api/generated';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourImSensitiveWordDenyRef = ref();
+// 表单数据定义
+type FormEditTourImSensitiveWordDenyData = {
+  TourImSensitiveWordDeny: TourImSensitiveWordDenyData;
+};
+// 表单数据
+const formData = reactive<FormEditTourImSensitiveWordDenyData>({
+  TourImSensitiveWordDeny: {
+    // id
+    id: undefined,
+    // 黑名单词汇
+    word: undefined,
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourImSensitiveWordDeny.word': [
+    {required: true, message: '请输入黑名单词汇', trigger: 'blur'}
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourImSensitiveWordDenyData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourImSensitiveWordDeny = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourImSensitiveWordDenyController.view(params).then(res => {
+      formData.TourImSensitiveWordDeny = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourImSensitiveWordDeny = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourImSensitiveWordDeny = () => {
+  refreshFormEditTourImSensitiveWordDeny();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourImSensitiveWordDeny();
+};
+/**
+ * 保存
+ */
+const onSubmitTourImSensitiveWordDenyClick = () => {
+  formEditTourImSensitiveWordDenyRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourImSensitiveWordDeny
+      };
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourImSensitiveWordDenyDto: {
+        id: formData.TourImSensitiveWordDeny.id,
+        word: formData.TourImSensitiveWordDeny.word,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourImSensitiveWordDenyController.update : TourImSensitiveWordDenyController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourImSensitiveWordDenyData().then(res => {
+    if (isEdit.value) refreshFormEditTourImSensitiveWordDeny();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 365 - 0
src/pages/SensitiveWord/formTourImSensitiveWordAllow.vue

@@ -0,0 +1,365 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourImSensitiveWordAllowRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourImSensitiveWordAllow()" @reset="resetFormTourImSensitiveWordAllow">
+        <el-form-item label="白名单词汇">
+          <el-input
+            class="filter-item"
+            v-model="formFilter.wordFilter"
+            type="text"
+            placeholder=""
+            :clearable="true"
+            :show-word-limit="false"
+            maxlength=""
+          />
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourImSensitiveWordAllowTable"
+      class="page-table"
+      :data="formTourImSensitiveWordAllowTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourImSensitiveWordAllowTableWidgetCurrentPage - 1) * formTourImSensitiveWordAllowTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourImSensitiveWordAllowTableWidget.onSortChange"
+      @refresh="formTourImSensitiveWordAllowTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImSensitiveWordAllow:formTourImSensitiveWordAllow:addTourImSensitiveWordAllow')"
+          @click="onAddTourImSensitiveWordAllowClick()"
+          >
+          新建
+        </el-button>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImSensitiveWordAllow:formTourImSensitiveWordAllow:exportTourImSensitiveWordAllow')"
+          @click="onExportTourImSensitiveWordAllowClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :disabled="!checkPermCodeExist('formTourImSensitiveWordAllow:formTourImSensitiveWordAllow:importTourImSensitiveWordAllow')"
+          :on-change="onImportTourImSensitiveWordAllowClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+              :disabled="!checkPermCodeExist('formTourImSensitiveWordAllow:formTourImSensitiveWordAllow:importTourImSensitiveWordAllow')"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourImSensitiveWordAllowTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="白名单词汇" field="word" />
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onEditTourImSensitiveWordAllowClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImSensitiveWordAllow:formTourImSensitiveWordAllow:editTourImSensitiveWordAllow')"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onDeleteTourImSensitiveWordAllowClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImSensitiveWordAllow:formTourImSensitiveWordAllow:deleteTourImSensitiveWordAllow')"
+          >
+            删除
+          </el-button>
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourImSensitiveWordAllowTableWidgetTotalCount"
+            :current-page="formTourImSensitiveWordAllowTableWidgetCurrentPage"
+            :page-size="formTourImSensitiveWordAllowTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourImSensitiveWordAllowTableWidget.onCurrentPageChange"
+            @size-change="formTourImSensitiveWordAllowTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourImSensitiveWordAllow',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImSensitiveWordAllowData } from '@/api/generated/tourImSensitiveWordAllowController';
+import { TourImSensitiveWordAllowController } from '@/api/generated';
+import FormEditTourImSensitiveWordAllow from '@/pages/SensitiveWord/formEditTourImSensitiveWordAllow.vue';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+  }>(),
+  {
+    subPage: 0,
+  },
+);
+
+const formFilter = reactive({
+  // 白名单词汇
+  wordFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 白名单词汇
+  wordFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourImSensitiveWordAllow();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourImSensitiveWordAllowTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourImSensitiveWordAllowDtoFilter: {
+      word: formFilter.wordFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourImSensitiveWordAllowController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourImSensitiveWordAllowTableVerify = () => {
+  formFilterCopy.wordFilter = formFilter.wordFilter;
+  return true;
+};
+/**
+ * 新建
+ */
+const onAddTourImSensitiveWordAllowClick = (row?: TourImSensitiveWordAllowData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  Dialog
+    .show('新建', FormEditTourImSensitiveWordAllow, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImSensitiveWordAllowTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 编辑
+ */
+const onEditTourImSensitiveWordAllowClick = (row?: TourImSensitiveWordAllowData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  Dialog
+    .show('编辑', FormEditTourImSensitiveWordAllow, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImSensitiveWordAllowTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourImSensitiveWordAllowClick = (row?: TourImSensitiveWordAllowData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourImSensitiveWordAllowController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourImSensitiveWordAllowClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourImSensitiveWordAllowController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourImSensitiveWordAllowTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourImSensitiveWordAllowClick = (row?: TourImSensitiveWordAllowData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourImSensitiveWordAllowController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourImSensitiveWordAllowTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourImSensitiveWordAllowTableOptions: TableOptions<TourImSensitiveWordAllowData> = {
+  loadTableData: loadFormTourImSensitiveWordAllowTableWidgetData,
+  verifyTableParameter: loadFormTourImSensitiveWordAllowTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourImSensitiveWordAllowTable = ref();
+const formTourImSensitiveWordAllowTableWidget = useTable(formTourImSensitiveWordAllowTableOptions);
+const {
+  dataList: formTourImSensitiveWordAllowTableWidgetDataList,
+  currentPage: formTourImSensitiveWordAllowTableWidgetCurrentPage,
+  pageSize: formTourImSensitiveWordAllowTableWidgetPageSize,
+  totalCount: formTourImSensitiveWordAllowTableWidgetTotalCount,
+} = formTourImSensitiveWordAllowTableWidget;
+const refreshFormTourImSensitiveWordAllow = () => {
+  // 刷新段落
+  formTourImSensitiveWordAllowTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourImSensitiveWordAllow = () => {
+  formFilter.wordFilter = undefined;
+  formFilterCopy.wordFilter = undefined;
+  refreshFormTourImSensitiveWordAllow();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourImSensitiveWordAllow();
+};
+const formInit = () => {
+  refreshFormTourImSensitiveWordAllow();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script>

+ 365 - 0
src/pages/SensitiveWord/formTourImSensitiveWordDeny.vue

@@ -0,0 +1,365 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourImSensitiveWordDenyRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourImSensitiveWordDeny()" @reset="resetFormTourImSensitiveWordDeny">
+        <el-form-item label="黑名单词汇">
+          <el-input
+            class="filter-item"
+            v-model="formFilter.wordFilter"
+            type="text"
+            placeholder=""
+            :clearable="true"
+            :show-word-limit="false"
+            maxlength=""
+          />
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourImSensitiveWordDenyTable"
+      class="page-table"
+      :data="formTourImSensitiveWordDenyTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourImSensitiveWordDenyTableWidgetCurrentPage - 1) * formTourImSensitiveWordDenyTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourImSensitiveWordDenyTableWidget.onSortChange"
+      @refresh="formTourImSensitiveWordDenyTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImSensitiveWordDeny:formTourImSensitiveWordDeny:addTourImSensitiveWordDeny')"
+          @click="onAddTourImSensitiveWordDenyClick()"
+          >
+          新建
+        </el-button>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourImSensitiveWordDeny:formTourImSensitiveWordDeny:exportTourImSensitiveWordDeny')"
+          @click="onExportTourImSensitiveWordDenyClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :disabled="!checkPermCodeExist('formTourImSensitiveWordDeny:formTourImSensitiveWordDeny:importTourImSensitiveWordDeny')"
+          :on-change="onImportTourImSensitiveWordDenyClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+              :disabled="!checkPermCodeExist('formTourImSensitiveWordDeny:formTourImSensitiveWordDeny:importTourImSensitiveWordDeny')"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourImSensitiveWordDenyTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="黑名单词汇" field="word" />
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onEditTourImSensitiveWordDenyClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImSensitiveWordDeny:formTourImSensitiveWordDeny:editTourImSensitiveWordDeny')"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onDeleteTourImSensitiveWordDenyClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourImSensitiveWordDeny:formTourImSensitiveWordDeny:deleteTourImSensitiveWordDeny')"
+          >
+            删除
+          </el-button>
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourImSensitiveWordDenyTableWidgetTotalCount"
+            :current-page="formTourImSensitiveWordDenyTableWidgetCurrentPage"
+            :page-size="formTourImSensitiveWordDenyTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourImSensitiveWordDenyTableWidget.onCurrentPageChange"
+            @size-change="formTourImSensitiveWordDenyTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourImSensitiveWordDeny',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourImSensitiveWordDenyData } from '@/api/generated/tourImSensitiveWordDenyController';
+import { TourImSensitiveWordDenyController } from '@/api/generated';
+import FormEditTourImSensitiveWordDeny from '@/pages/SensitiveWord/formEditTourImSensitiveWordDeny.vue';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const { 
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+  }>(),
+  {
+    subPage: 0,
+  },
+);
+
+const formFilter = reactive({
+  // 黑名单词汇
+  wordFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 黑名单词汇
+  wordFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourImSensitiveWordDeny();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourImSensitiveWordDenyTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourImSensitiveWordDenyDtoFilter: {
+      word: formFilter.wordFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourImSensitiveWordDenyController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourImSensitiveWordDenyTableVerify = () => {
+  formFilterCopy.wordFilter = formFilter.wordFilter;
+  return true;
+};
+/**
+ * 新建
+ */
+const onAddTourImSensitiveWordDenyClick = (row?: TourImSensitiveWordDenyData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  Dialog
+    .show('新建', FormEditTourImSensitiveWordDeny, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImSensitiveWordDenyTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 编辑
+ */
+const onEditTourImSensitiveWordDenyClick = (row?: TourImSensitiveWordDenyData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  Dialog
+    .show('编辑', FormEditTourImSensitiveWordDeny, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourImSensitiveWordDenyTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourImSensitiveWordDenyClick = (row?: TourImSensitiveWordDenyData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourImSensitiveWordDenyController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourImSensitiveWordDenyClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourImSensitiveWordDenyController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourImSensitiveWordDenyTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourImSensitiveWordDenyClick = (row?: TourImSensitiveWordDenyData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourImSensitiveWordDenyController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourImSensitiveWordDenyTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourImSensitiveWordDenyTableOptions: TableOptions<TourImSensitiveWordDenyData> = {
+  loadTableData: loadFormTourImSensitiveWordDenyTableWidgetData,
+  verifyTableParameter: loadFormTourImSensitiveWordDenyTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourImSensitiveWordDenyTable = ref();
+const formTourImSensitiveWordDenyTableWidget = useTable(formTourImSensitiveWordDenyTableOptions);
+const {
+  dataList: formTourImSensitiveWordDenyTableWidgetDataList,
+  currentPage: formTourImSensitiveWordDenyTableWidgetCurrentPage,
+  pageSize: formTourImSensitiveWordDenyTableWidgetPageSize,
+  totalCount: formTourImSensitiveWordDenyTableWidgetTotalCount,
+} = formTourImSensitiveWordDenyTableWidget;
+const refreshFormTourImSensitiveWordDeny = () => {
+  // 刷新段落
+  formTourImSensitiveWordDenyTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourImSensitiveWordDeny = () => {
+  formFilter.wordFilter = undefined;
+  formFilterCopy.wordFilter = undefined;
+  refreshFormTourImSensitiveWordDeny();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourImSensitiveWordDeny();
+};
+const formInit = () => {
+  refreshFormTourImSensitiveWordDeny();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script>

+ 363 - 0
src/pages/TourBrowseRecords/formTourBrowseRecords.vue

@@ -0,0 +1,363 @@
+<!-- <template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourBrowseRecordsRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourBrowseRecords()" @reset="resetFormTourBrowseRecords">
+        <el-form-item label="浏览者id">
+          <el-input-number
+            class="filter-item"
+            v-model="formFilter.visitorIdFilter"
+            placeholder=""
+            :clearable="true"
+            :step="1"
+            :controls="true"
+          />
+        </el-form-item>
+        <el-form-item label="浏览项目类型">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.itemTypeIdFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="itemTypeIdFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in itemTypeIdFilterWidgetDropdownList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourBrowseRecordsTable"
+      class="page-table"
+      :data="formTourBrowseRecordsTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourBrowseRecordsTableWidgetCurrentPage - 1) * formTourBrowseRecordsTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourBrowseRecordsTableWidget.onSortChange"
+      @refresh="formTourBrowseRecordsTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          :disabled="!checkPermCodeExist('formTourBrowseRecords:formTourBrowseRecords:exportTourBrowseRecords')"
+          @click="onExportTourBrowseRecordsClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :disabled="!checkPermCodeExist('formTourBrowseRecords:formTourBrowseRecords:importTourBrowseRecords')"
+          :on-change="onImportTourBrowseRecordsClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+              :disabled="!checkPermCodeExist('formTourBrowseRecords:formTourBrowseRecords:importTourBrowseRecords')"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourBrowseRecordsTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="浏览者id" field="visitorId" />
+      <vxe-column title="项目id" field="itemId" />
+      <vxe-column title="浏览项目类型" field="itemTypeIdDictMap.name" />
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onDeleteTourBrowseRecordsClick(scope.row)"
+            :disabled="!checkPermCodeExist('formTourBrowseRecords:formTourBrowseRecords:deleteTourBrowseRecords')"
+          >
+            删除
+          </el-button>
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourBrowseRecordsTableWidgetTotalCount"
+            :current-page="formTourBrowseRecordsTableWidgetCurrentPage"
+            :page-size="formTourBrowseRecordsTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourBrowseRecordsTableWidget.onCurrentPageChange"
+            @size-change="formTourBrowseRecordsTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourBrowseRecords',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourBrowseRecordsData } from '@/api/generated/tourBrowseRecordsController';
+import { TourBrowseRecordsController } from '@/api/generated';
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const {
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+  }>(),
+  {
+    subPage: 0,
+  },
+);
+
+const formFilter = reactive({
+  // 浏览者id
+  visitorIdFilter: undefined,
+  // 浏览项目类型
+  itemTypeIdFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 浏览者id
+  visitorIdFilter: undefined,
+  // 浏览项目类型
+  itemTypeIdFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourBrowseRecords();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourBrowseRecordsTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourBrowseRecordsDtoFilter: {
+      visitorId: formFilter.visitorIdFilter,
+      itemTypeId: formFilter.itemTypeIdFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourBrowseRecordsController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourBrowseRecordsTableVerify = () => {
+  formFilterCopy.visitorIdFilter = formFilter.visitorIdFilter;
+  formFilterCopy.itemTypeIdFilter = formFilter.itemTypeIdFilter;
+  return true;
+};
+/**
+ * 导出
+ */
+const onExportTourBrowseRecordsClick = (row?: TourBrowseRecordsData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourBrowseRecordsController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourBrowseRecordsClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourBrowseRecordsController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourBrowseRecordsTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourBrowseRecordsClick = (row?: TourBrowseRecordsData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourBrowseRecordsController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourBrowseRecordsTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourBrowseRecordsTableOptions: TableOptions<TourBrowseRecordsData> = {
+  loadTableData: loadFormTourBrowseRecordsTableWidgetData,
+  verifyTableParameter: loadFormTourBrowseRecordsTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourBrowseRecordsTable = ref();
+const formTourBrowseRecordsTableWidget = useTable(formTourBrowseRecordsTableOptions);
+const {
+  dataList: formTourBrowseRecordsTableWidgetDataList,
+  currentPage: formTourBrowseRecordsTableWidgetCurrentPage,
+  pageSize: formTourBrowseRecordsTableWidgetPageSize,
+  totalCount: formTourBrowseRecordsTableWidgetTotalCount,
+} = formTourBrowseRecordsTableWidget;
+/**
+ * 浏览项目类型下拉数据获取函数
+ */
+const loadItemTypeIdFilterDropdownList = (): Promise<ListData<DictData>> => {
+  return new Promise((resolve, reject) => {
+    DictionaryController.dictGlobalDict({ dictCode: 'ViewItemType', itemIdType: 'Integer' }).then(res => {
+      resolve({
+        dataList: res.getList(),
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 浏览项目类型配置参数
+const itemTypeIdFilterOptions: DropdownOptions<DictData> = {
+  loadData: loadItemTypeIdFilterDropdownList,
+  isTree: false,
+};
+// 浏览项目类型下拉组件
+const itemTypeIdFilterWidget = useDropdown(itemTypeIdFilterOptions);
+const { dropdownList: itemTypeIdFilterWidgetDropdownList } = itemTypeIdFilterWidget
+const refreshFormTourBrowseRecords = () => {
+  // 刷新段落
+  formTourBrowseRecordsTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourBrowseRecords = () => {
+  formFilter.visitorIdFilter = undefined;
+  formFilterCopy.visitorIdFilter = undefined;
+  formFilter.itemTypeIdFilter = undefined;
+  formFilterCopy.itemTypeIdFilter = undefined;
+  refreshFormTourBrowseRecords();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourBrowseRecords();
+};
+const formInit = () => {
+  itemTypeIdFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  refreshFormTourBrowseRecords();
+};
+
+onMounted(() => {
+  formInit();
+});
+
+onActivated(() => {
+  onResume();
+});
+</script> -->

+ 319 - 0
src/pages/tourFans/formEditTourFans.vue

@@ -0,0 +1,319 @@
+<template>
+  <div class="dialog-box" style="position: relative">
+    <el-scrollbar class="custom-scroll content-box">
+      <el-form
+        ref="formEditTourFansRef"
+        :model="formData"
+        :size="layoutStore.defaultFormItemSize"
+        :rules="rules"
+        label-width="120px"
+        label-position="right"
+        @submit.prevent
+      >
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="被关注人" prop="TourFans.attentionId">
+              <el-select
+                class="input-item"
+                v-model="formData.TourFans.attentionId"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="attentionIdWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in attentionIdWidgetDropdownList"
+                  :key="item.userId"
+                  :label="item.showName"
+                  :value="item.userId"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="关注人" prop="TourFans.createUserId">
+              <el-select
+                class="input-item"
+                v-model="formData.TourFans.createUserId"
+                placeholder=""
+                :clearable="true"
+                :filterable="true"
+                @visible-change="createUserIdWidget.onVisibleChange"
+              >
+                <el-option
+                  v-for="item in createUserIdWidgetDropdownList"
+                  :key="item.userId"
+                  :label="item.showName"
+                  :value="item.userId"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-scrollbar>
+    <el-row class="footer-box" type="flex" justify="end" align="middle">
+      <el-button :size="layoutStore.defaultFormItemSize" @click="onCancel()">取消</el-button>
+      <el-button :size="layoutStore.defaultFormItemSize" type="primary" @click="onSubmitTourFansClick()">保存</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formEditTourFans',
+};
+</script>
+
+<script setup lang="ts">
+import { DialogProp } from '@/components/Dialog/types';
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { DictionaryController } from '@/api/system';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourFansData } from '@/api/generated/tourFansController';
+import {TourFansController, TourUserController} from '@/api/generated';
+import TourUser from "@/types/table/tourUser";
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const {
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+    id?: ANY_OBJECT;
+    saveOnSubmit?: boolean;
+    rowData?: ANY_OBJECT;
+    // 当使用Dialog.show弹出组件时,须定义该prop属性,以便对dialog进行回调
+    dialog?: DialogProp<ANY_OBJECT[]>;
+  }>(),
+  {
+    subPage: 0,
+    id: undefined,
+    saveOnSubmit: true,
+    rowData: undefined,
+    dialog: undefined,
+  },
+);
+
+const formEditTourFansRef = ref();
+// 表单数据定义
+type FormEditTourFansData = {
+  TourFans: TourFansData;
+};
+// 表单数据
+const formData = reactive<FormEditTourFansData>({
+  TourFans: {
+    // 粉丝表id
+    id: undefined,
+    // 被关注人id
+    attentionId: undefined,
+    // 是否删除
+    deleteFlag: undefined,
+    // 创建人id
+    createUserId: undefined,
+    // 创建时间
+    createTime: undefined,
+    // 更新人id
+    updateUserId: undefined,
+    // 更新时间
+    updateTime: undefined,
+  },
+},
+);
+// 表单验证规则
+const rules = reactive({
+  'TourFans.attentionId': [
+    {required: true, message: '请输入被关注人', trigger: 'blur'}
+  ],
+  'TourFans.createUserId': [
+  ],
+});
+
+const onCancel = () => {
+  if (props.dialog) {
+    props.dialog.cancel();
+  }
+};
+
+const isEdit = computed(() => {
+  return props.saveOnSubmit ? props.id != null : props.rowData != null;
+});
+
+// 初始化页面数据
+const loadTourFansData = () => {
+  return new Promise<void>((resolve, reject) => {
+    if (!isEdit.value) {
+      resolve();
+      return;
+    }
+    if (!props.saveOnSubmit && props.rowData != null) {
+      formData.TourFans = JSON.parse(JSON.stringify(props.rowData));
+      resolve();
+      return;
+    }
+    let params: ANY_OBJECT = {
+      id: props.id
+    };
+    TourFansController.view(params).then(res => {
+      formData.TourFans = { ...res.data };
+      resolve();
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 被关注人下拉数据获取函数
+ */
+const loadAttentionIdDropdownList = (): Promise<ListData<TourUser>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    TourUserController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 被关注人配置参数
+const attentionIdOptions: DropdownOptions<TourUser> = {
+  loadData: loadAttentionIdDropdownList,
+  isTree: false,
+};
+// 被关注人下拉组件
+const attentionIdWidget = useDropdown(attentionIdOptions);
+const { dropdownList: attentionIdWidgetDropdownList } = attentionIdWidget
+/**
+ * 关注人下拉数据获取函数
+ */
+const loadCreateUserIdDropdownList = (): Promise<ListData<TourUser>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    TourUserController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 关注人配置参数
+const createUserIdOptions: DropdownOptions<TourUser> = {
+  loadData: loadCreateUserIdDropdownList,
+  isTree: false,
+};
+// 关注人下拉组件
+const createUserIdWidget = useDropdown(createUserIdOptions);
+const { dropdownList: createUserIdWidgetDropdownList } = createUserIdWidget
+const onUploadError = () => {
+  ElMessage.error('文件上传失败');
+};
+const onUploadLimit = () => {
+  ElMessage.error('已经超出最大上传个数限制');
+};
+const refreshFormEditTourFans = () => {
+  // 刷新段落
+};
+/**
+ * 重置过滤值
+ */
+const resetFormEditTourFans = () => {
+  refreshFormEditTourFans();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormEditTourFans();
+};
+/**
+ * 保存
+ */
+const onSubmitTourFansClick = () => {
+  formEditTourFansRef.value.validate((valid) => {
+    if (!valid) return;
+    // 级联操作
+    if (!props.saveOnSubmit) {
+      let retFormData = {
+        ...formData.TourFans
+      };
+    retFormData.attentionIdDictMap = attentionIdWidgetDropdownList.value.filter(item=>item.userId == formData.TourFans.attentionId)[0];
+    retFormData.createUserIdDictMap = createUserIdWidgetDropdownList.value.filter(item=>item.userId == formData.TourFans.createUserId)[0];
+  props.dialog?.submit(retFormData);
+      return;
+    }
+    let params: ANY_OBJECT = {
+      tourFansDto: {
+        id: formData.TourFans.id,
+        attentionId: formData.TourFans.attentionId,
+        createUserId: formData.TourFans.createUserId,
+        createTime: formData.TourFans.createTime,
+        updateUserId: formData.TourFans.updateUserId,
+        updateTime: formData.TourFans.updateTime,
+      }
+    };
+
+    let httpCall = isEdit.value ? TourFansController.update : TourFansController.add;
+    httpCall(params).then(res => {
+      ElMessage.success('保存成功');
+      props.dialog?.submit();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  });
+};
+const formInit = () => {
+  loadTourFansData().then(res => {
+    attentionIdWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    createUserIdWidget.onVisibleChange(true).catch(e => {console.error(e)});
+    if (isEdit.value) refreshFormEditTourFans();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 437 - 0
src/pages/tourFans/formTourFans.vue

@@ -0,0 +1,437 @@
+<template>
+  <div class="page-box" style="position: relative;">
+    <el-form
+      ref="formTourFansRef"
+      :size="layoutStore.defaultFormItemSize"
+      label-width="120px"
+      label-position="right"
+      @submit.prevent
+    >
+      <filter-box :item-width="350" @search="refreshFormTourFans()" @reset="resetFormTourFans">
+        <el-form-item label="被关注人">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.attentionIdFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="attentionIdFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in attentionIdFilterWidgetDropdownList"
+              :key="item.userId"
+              :label="item.showName"
+              :value="item.userId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="关注人">
+          <el-select
+            class="filter-item"
+            v-model="formFilter.createUserIdFilter"
+            placeholder=""
+            :clearable="true"
+            :filterable="true"
+            @visible-change="createUserIdFilterWidget.onVisibleChange"
+          >
+            <el-option
+              v-for="item in createUserIdFilterWidgetDropdownList"
+              :key="item.userId"
+              :label="item.showName"
+              :value="item.userId"
+            />
+          </el-select>
+        </el-form-item>
+      </filter-box>
+    </el-form>
+    <table-box
+      ref="formTourFansTable"
+      class="page-table"
+      :data="formTourFansTableWidgetDataList"
+      :size="layoutStore.defaultFormItemSize"
+      :row-config="{isCurrent: false, isHover: true}"
+      :seq-config="{startIndex: ((formTourFansTableWidgetCurrentPage - 1) * formTourFansTableWidgetPageSize)}"
+      :sort-config="{remote: true}"
+      :hasExtend="true"
+      @sort-change="formTourFansTableWidget.onSortChange"
+      @refresh="formTourFansTableWidget.refreshTable()"
+    >
+      <template #operator>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          @click="onAddTourFansClick()"
+          >
+          新建
+        </el-button>
+        <el-button
+          type="primary"
+          :size="layoutStore.defaultFormItemSize"
+          @click="onExportTourFansClick()"
+          >
+          导出
+        </el-button>
+        <el-upload
+          class="btn-import"
+          :auto-upload="false"
+          action=""
+          :show-file-list="false"
+          accept=".xls,.xlsx"
+          style="display: inline-block;"
+          :on-change="onImportTourFansClick"
+        >
+          <template #trigger>
+            <el-button
+              type="primary"
+              :size="layoutStore.defaultFormItemSize"
+            >
+              导入
+            </el-button>
+          </template>
+        </el-upload>
+      </template>
+      <vxe-column title="序号" type="seq" :index="formTourFansTableWidget.getTableIndex" :width="80" />
+      <vxe-column title="被关注人" field="attentionIdDictMap.name" />
+      <vxe-column title="关注人" field="createUserIdDictMap.name" />
+      <vxe-column title="操作" fixed="right">
+        <template v-slot="scope">
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onEditTourFansClick(scope.row)"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            :size="layoutStore.defaultFormItemSize"
+            @click.stop="onDeleteTourFansClick(scope.row)"
+          >
+            删除
+          </el-button>
+        </template>
+      </vxe-column>
+      <template slot="empty">
+        <div class="table-empty unified-font">
+          <img src="@/assets/img/empty.png">
+          <span>暂无数据</span>
+        </div>
+      </template>
+      <!-- 分页 -->
+      <template #pagination>
+        <el-row type="flex" justify="end" style="margin-top: 10px;">
+          <el-pagination
+            :total="formTourFansTableWidgetTotalCount"
+            :current-page="formTourFansTableWidgetCurrentPage"
+            :page-size="formTourFansTableWidgetPageSize"
+            :page-sizes="[10, 20, 50, 100]"
+            layout="total, prev, pager, next, sizes"
+            @current-change="formTourFansTableWidget.onCurrentPageChange"
+            @size-change="formTourFansTableWidget.onPageSizeChange">
+          </el-pagination>
+        </el-row>
+      </template>
+    </table-box>
+    <label v-if="subPage" class="page-close-box" @click="onCancel()">
+      <img src="@/assets/img/back2.png" alt="">
+    </label>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'formTourFans',
+};
+</script>
+
+<script setup lang="ts">
+import { VxeColumn, VxeTable } from 'vxe-table';
+import { ANY_OBJECT } from '@/types/generic';
+import { DictData, DictionaryBase } from '@/common/staticDict/types';
+import { ElMessage, ElMessageBox, UploadFile } from 'element-plus';
+import { useRoute, useRouter } from 'vue-router';
+import { useCommon } from '@/common/hooks/useCommon';
+import { useLayoutStore, useStaticDictStore } from '@/store';
+import { useDownload } from '@/common/hooks/useDownload';
+import { useDropdown } from '@/common/hooks/useDropdown';
+import { DropdownOptions, ListData } from '@/common/types/list';
+import { useTable } from '@/common/hooks/useTable';
+import { TableOptions } from '@/common/types/pagination';
+import { useUpload } from '@/common/hooks/useUpload';
+import { useUploadWidget } from '@/common/hooks/useUploadWidget';
+import { treeDataTranslate, findItemFromList, findTreeNodePath, findTreeNode, stringCase } from '@/common/utils';
+import { TourFansData } from '@/api/generated/tourFansController';
+import { TourFansController,TourUserController } from '@/api/generated';
+import FormEditTourFans from '@/pages/tourFans/formEditTourFans.vue';
+import {TourVisaData} from "@/api/generated/tourVisaController";
+import TourUser from "@/types/table/tourUser";
+
+const router = useRouter();
+const route = useRoute();
+const layoutStore = useLayoutStore();
+const { downloadFile } = useDownload();
+const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
+const {
+  Delete,
+  Search,
+  Edit,
+  Plus,
+  Refresh,
+  Picture,
+  Dialog,
+  mainContextHeight,
+  clientHeight,
+  checkPermCodeExist,
+  parseParams,
+  parseArrayParams,
+  formatDateByStatsType,
+  getDateRangeFilter,
+} = useCommon();
+// 静态字典
+const { staticDict: StaticDict } = useStaticDictStore();
+
+const props = withDefaults(
+  defineProps<{
+    subPage?: number | string | boolean;
+  }>(),
+  {
+    subPage: 0,
+  },
+);
+
+const formFilter = reactive({
+  // 被关注人
+  attentionIdFilter: undefined,
+  // 关注人
+  createUserIdFilter: undefined,
+});
+const formFilterCopy = reactive({
+  // 被关注人
+  attentionIdFilter: undefined,
+  // 关注人
+  createUserIdFilter: undefined,
+});
+
+const onCancel = () => {
+  router.go(-1);
+  layoutStore.removeCachePage(route.fullPath as string);
+  route.meta.refreshParentCachedPage = true;
+};
+
+const onResume = () => {
+  refreshFormTourFans();
+};
+
+/**
+ * 表格组件数据获取函数,返回Promise
+ */
+const loadFormTourFansTableWidgetData = (params: ANY_OBJECT) => {
+  if (params == null) params = {};
+  params = {
+    ...params,
+    tourFansDtoFilter: {
+      attentionId: formFilter.attentionIdFilter,
+      createUserId: formFilter.createUserIdFilter,
+    }
+  };
+  return new Promise((resolve, reject) => {
+    TourFansController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+        totalCount: res.data.totalCount
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+/**
+ * 表格组件数据获取检测函数,返回true正常获取数据,返回false停止获取数据
+ */
+const loadFormTourFansTableVerify = () => {
+  formFilterCopy.attentionIdFilter = formFilter.attentionIdFilter;
+  formFilterCopy.createUserIdFilter = formFilter.createUserIdFilter;
+  return true;
+};
+/**
+ * 新建
+ */
+const onAddTourFansClick = (row?: TourFansData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  Dialog
+    .show('新建', FormEditTourFans, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourFansTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 编辑
+ */
+const onEditTourFansClick = (row?: TourFansData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+  Dialog
+    .show('编辑', FormEditTourFans, { area: '900px' }, { ...params, subPage: true })
+    .then(res => {
+      formTourFansTableWidget.refreshTable();
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+};
+/**
+ * 导出
+ */
+const onExportTourFansClick = (row?: TourFansData) => {
+  let params: ANY_OBJECT = {
+  };
+
+  TourFansController.export(params, '表格组件.xlsx').then(res => {
+    ElMessage.success('导出成功');
+  }).catch(e => {
+    ElMessage.error(e.errorMessage);
+  });
+};
+/**
+ * 导入
+ */
+const onImportTourFansClick = (file) => {
+  let params: ANY_OBJECT = {
+    importFile: file.raw,
+    // 是否忽略表头
+    skipHeader: false
+  };
+
+  TourFansController.import(params).then(res => {
+    ElMessage.success('导入成功');
+    formTourFansTableWidget.refreshTable();
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+/**
+ * 删除
+ */
+const onDeleteTourFansClick = (row?: TourFansData) => {
+  let params: ANY_OBJECT = {
+    id: row?.id,
+  };
+
+  ElMessageBox.confirm('是否删除此记录?').then(res => {
+    TourFansController.delete(params).then(res => {
+      ElMessage.success('删除成功');
+      formTourFansTableWidget.refreshTable(false, 1);
+    }).catch(e => {
+      // TODO: 异常处理
+      console.error(e);
+    });
+  }).catch(e => {
+    // TODO: 异常处理
+    console.error(e);
+  });
+};
+// 表格组件表格组件参数
+const formTourFansTableOptions: TableOptions<TourFansData> = {
+  loadTableData: loadFormTourFansTableWidgetData,
+  verifyTableParameter: loadFormTourFansTableVerify,
+  paged: true,
+  rowSelection: false,
+  orderFieldName: undefined,
+  ascending: true,
+};
+// 表格组件表格组件
+const formTourFansTable = ref();
+const formTourFansTableWidget = useTable(formTourFansTableOptions);
+const {
+  dataList: formTourFansTableWidgetDataList,
+  currentPage: formTourFansTableWidgetCurrentPage,
+  pageSize: formTourFansTableWidgetPageSize,
+  totalCount: formTourFansTableWidgetTotalCount,
+} = formTourFansTableWidget;
+/**
+ * 被关注人下拉数据获取函数
+ */
+const loadAttentionIdFilterDropdownList = (): Promise<ListData<TourUser>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    TourUserController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 被关注人配置参数
+const attentionIdFilterOptions: DropdownOptions<TourUser> = {
+  loadData: loadAttentionIdFilterDropdownList,
+  isTree: false,
+};
+// 被关注人下拉组件
+const attentionIdFilterWidget = useDropdown(attentionIdFilterOptions);
+const { dropdownList: attentionIdFilterWidgetDropdownList } = attentionIdFilterWidget
+/**
+ * 关注人下拉数据获取函数
+ */
+const loadCreateUserIdFilterDropdownList = (): Promise<ListData<TourUser>> => {
+  return new Promise((resolve, reject) => {
+    const params = {
+    };
+    TourUserController.list(params).then(res => {
+      resolve({
+        dataList: res.data.dataList,
+      });
+    }).catch(e => {
+      reject(e);
+    });
+  });
+};
+// 关注人配置参数
+const createUserIdFilterOptions: DropdownOptions<TourUser> = {
+  loadData: loadCreateUserIdFilterDropdownList,
+  isTree: false,
+};
+// 关注人下拉组件
+const createUserIdFilterWidget = useDropdown(createUserIdFilterOptions);
+const { dropdownList: createUserIdFilterWidgetDropdownList } = createUserIdFilterWidget
+const refreshFormTourFans = () => {
+  // 刷新段落
+  formTourFansTableWidget.refreshTable();
+};
+/**
+ * 重置过滤值
+ */
+const resetFormTourFans = () => {
+  formFilter.attentionIdFilter = undefined;
+  formFilterCopy.attentionIdFilter = undefined;
+  formFilter.createUserIdFilter = undefined;
+  formFilterCopy.createUserIdFilter = undefined;
+  refreshFormTourFans();
+};
+/**
+ * 重置所有过滤值
+ */
+const resetFilter = () => {
+  resetFormTourFans();
+};
+const formInit = () => {
+  attentionIdFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  createUserIdFilterWidget.onVisibleChange(true).catch(e => {console.error(e)});
+  refreshFormTourFans();
+};
+
+onMounted(() => {
+  formInit();
+});
+</script>

+ 2 - 2
src/pages/tourHouseRentInfo/formTourHouseRentInfo.vue

@@ -291,7 +291,7 @@ const route = useRoute();
 const layoutStore = useLayoutStore();
 const { downloadFile } = useDownload();
 const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
-const { 
+const {
   Delete,
   Search,
   Edit,
@@ -722,4 +722,4 @@ const formInit = () => {
 onMounted(() => {
   formInit();
 });
-</script>
+</script>

+ 2 - 2
src/pages/tourOrder/formTourOrder.vue

@@ -251,7 +251,7 @@ const route = useRoute();
 const layoutStore = useLayoutStore();
 const { downloadFile } = useDownload();
 const { getUploadHeaders, getUploadActionUrl, fileListToJson, parseUploadData, getPictureList } = useUpload();
-const { 
+const {
   Delete,
   Search,
   Edit,
@@ -534,4 +534,4 @@ const formInit = () => {
 onMounted(() => {
   formInit();
 });
-</script>
+</script>

+ 77 - 0
src/router/systemRouters.ts

@@ -635,6 +635,83 @@ export const routers: Array<RouteRecordRaw> = [
         props: getProps,
         meta: {title: '拼团返利记录', keepalive: true}
       },
+      {
+        path: 'formTourFans',
+        component: () => import('@/pages/tourFans/formTourFans.vue'),
+        name: 'formTourFans',
+        props: getProps,
+        meta: {title: '关注粉丝管理管理', keepalive: true}
+      },
+      // {
+      //   path: 'formTourBrowseRecords',
+      //   component: () => import('@/pages/TourBrowseRecords/formTourBrowseRecords.vue'),
+      //   name: 'formTourBrowseRecords',
+      //   props: getProps,
+      //   meta: {title: '用户浏览记录表管理', keepalive: true}
+      // },
+      {
+        path: 'formTourImComplaintType',
+        component: () => import('@/pages/ImComplaint/formTourImComplaintType.vue'),
+        name: 'formTourImComplaintType',
+        props: getProps,
+        meta: {title: '投诉类型管理管理', keepalive: true}
+      },
+      {
+        path: 'formTourImComplait',
+        component: () => import('@/pages/ImComplaint/formTourImComplait.vue'),
+        name: 'formTourImComplait',
+        props: getProps,
+        meta: {title: '投诉内容管理管理', keepalive: true}
+      },
+      {
+        path: 'formTourImSensitiveWordAllow',
+        component: () => import('@/pages/SensitiveWord/formTourImSensitiveWordAllow.vue'),
+        name: 'formTourImSensitiveWordAllow',
+        props: getProps,
+        meta: {title: '聊天敏感词白名单管理', keepalive: true}
+      },
+      {
+        path: 'formTourImSensitiveWordDeny',
+        component: () => import('@/pages/SensitiveWord/formTourImSensitiveWordDeny.vue'),
+        name: 'formTourImSensitiveWordDeny',
+        props: getProps,
+        meta: {title: '聊天敏感词黑名单管理', keepalive: true}
+      },
+      {
+        path: 'formTourImGroupType',
+        component: () => import('@/pages/ImGroup/formTourImGroupType.vue'),
+        name: 'formTourImGroupType',
+        props: getProps,
+        meta: {title: '聊天群聊类型管理管理', keepalive: true}
+      },
+      {
+        path: 'formTourImGroup',
+        component: () => import('@/pages/ImGroup/formTourImGroup.vue'),
+        name: 'formTourImGroup',
+        props: getProps,
+        meta: {title: '聊天群聊管理管理', keepalive: true}
+      },
+      {
+        path: 'formTourImGroupInvitation',
+        component: () => import('@/pages/ImGroup/formTourImGroupInvitation.vue'),
+        name: 'formTourImGroupInvitation',
+        props: getProps,
+        meta: {title: '聊天去聊邀请管理', keepalive: true}
+      },
+      {
+        path: 'formTourImMember',
+        component: () => import('@/pages/ImGroup/formTourImMember.vue'),
+        name: 'formTourImMember',
+        props: getProps,
+        meta: {title: '聊天群聊成员管理', keepalive: true}
+      },
+      {
+        path: 'formTourImMessage',
+        component: () => import('@/pages/ImGroup/formTourImMessage.vue'),
+        name: 'formTourImMessage',
+        props: getProps,
+        meta: {title: '聊天群聊消息管理', keepalive: true}
+      },
     ],
   },
   // TODO 第三方接入路由

+ 27 - 0
src/types/table/tourBrowseRecords.d.ts

@@ -0,0 +1,27 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourBrowseRecords {
+  // id
+  id?: number | undefined;
+  // 浏览者id
+  visitorId?: number | undefined;
+  // 原创游记作者id
+  writerId?: number | undefined;
+  // 游记、项目、签证id
+  itemId?: number | undefined;
+  // 浏览项目类型id  1游记 2项目 3签证
+  itemTypeId?: number | undefined;
+  itemTypeIdDictMap?: DictData,
+  // 删除标记 1正常 -1删除
+  dataState?: number | undefined;
+  // 创建人id
+  createUserId?: number | undefined;
+  // 创建时间
+  createTime?: string | undefined;
+  // 更新时间
+  updateTime?: string | undefined;
+  // 更新人id
+  updateUserId?: number | undefined;
+}
+
+export default TourBrowseRecords;

+ 23 - 0
src/types/table/tourFans.d.ts

@@ -0,0 +1,23 @@
+import { DictData } from '@/common/staticDict/types';
+import TourUser from "@/types/table/tourUser";
+
+interface TourFans {
+  // 粉丝表id
+  id?: number | undefined;
+  // 被关注人id
+  attentionId?: number | undefined;
+  attentionIdDictMap?: TourUser,
+  // 是否删除
+  deleteFlag?: number | undefined;
+  // 创建人id
+  createUserId?: number | undefined;
+  createUserIdDictMap?: TourUser,
+  // 创建时间
+  createTime?: string | undefined;
+  // 更新人id
+  updateUserId?: number | undefined;
+  // 更新时间
+  updateTime?: string | undefined;
+}
+
+export default TourFans;

+ 25 - 0
src/types/table/tourImComplaintType.d.ts

@@ -0,0 +1,25 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourImComplaintType {
+  // id
+  id?: number | undefined;
+  // 类型名称
+  typeName?: string | undefined;
+  // 类型描述
+  description?: string | undefined;
+  // 是否启用
+  enable?: number | undefined;
+  enableDictMap?: DictData,
+  // 删除标记 1正常 -1删除
+  dataState?: number | undefined;
+  // 创建人id
+  createUserId?: number | undefined;
+  // 创建时间
+  createTime?: string | undefined;
+  // 更新时间
+  updateTime?: string | undefined;
+  // 更新人id
+  updateUserId?: number | undefined;
+}
+
+export default TourImComplaintType;

+ 30 - 0
src/types/table/tourImComplait.d.ts

@@ -0,0 +1,30 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourImComplait {
+  // id
+  id?: number | undefined;
+  // 举报类型id
+  typeId?: number | undefined;
+  typeIdDictMap?: DictData,
+  // 类型为其他违规的理由
+  elseTypeReason?: string | undefined;
+  // 举报对象 1用户 2群组
+  objectType?: number | undefined;
+  objectTypeDictMap?: DictData,
+  // 举报描述
+  description?: string | undefined;
+  // 举报图片 最多三张
+  image?: string | undefined;
+  // 删除标记 1正常 -1删除
+  dataState?: number | undefined;
+  // 创建人id
+  createUserId?: number | undefined;
+  // 创建时间
+  createTime?: string | undefined;
+  // 更新时间
+  updateTime?: string | undefined;
+  // 更新人id
+  updateUserId?: number | undefined;
+}
+
+export default TourImComplait;

+ 44 - 0
src/types/table/tourImGroup.d.ts

@@ -0,0 +1,44 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourImGroup {
+  // id
+  id?: number | undefined;
+  // 群聊所属类型id
+  belongTypeId?: number | undefined;
+  belongTypeIdDictMap?: DictData,
+  // 群主id
+  leaderId?: number | undefined;
+  leaderIdDictMap?: DictData,
+  // 群聊名称
+  groupName?: string | undefined;
+  // 群聊头像
+  groupAvatar?: string | undefined;
+  // 群聊描述
+  description?: string | undefined;
+  // 是否公开展示 0隐藏 1公开
+  isPublic?: number | undefined;
+  isPublicDictMap?: DictData,
+  // 是否开启群聊邀请确认 0不开启 1开启
+  isNeedConfirm?: number | undefined;
+  isNeedConfirmDictMap?: DictData,
+  // 通知分类 0单聊 1群聊
+  noticeType?: number | undefined;
+  noticeTypeDictMap?: DictData,
+  // 是否封禁 0封禁 1正常
+  bannedStatus?: number | undefined;
+  bannedStatusDictMap?: DictData,
+  // 热度值
+  hotValue?: number | undefined;
+  // 删除标记 1正常 -1删除
+  dataState?: number | undefined;
+  // 创建人id
+  createUserId?: number | undefined;
+  // 创建时间
+  createTime?: string | undefined;
+  // 更新时间
+  updateTime?: string | undefined;
+  // 更新人id
+  updateUserId?: number | undefined;
+}
+
+export default TourImGroup;

+ 30 - 0
src/types/table/tourImGroupInvitation.d.ts

@@ -0,0 +1,30 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourImGroupInvitation {
+  // id
+  id?: number | undefined;
+  // 聊天群组id
+  groupId?: number | undefined;
+  groupIdDictMap?: DictData,
+  // 邀请人id
+  inviter?: number | undefined;
+  inviterDictMap?: DictData,
+  // 被邀请人id
+  invitee?: number | undefined;
+  inviteeDictMap?: DictData,
+  // 邀请状态 1待确认 2同意 3不同意
+  status?: number | undefined;
+  statusDictMap?: DictData,
+  // 删除标记 1正常 -1删除
+  dataState?: number | undefined;
+  // 创建人id
+  createUserId?: number | undefined;
+  // 创建时间
+  createTime?: string | undefined;
+  // 更新时间
+  updateTime?: string | undefined;
+  // 更新人id
+  updateUserId?: number | undefined;
+}
+
+export default TourImGroupInvitation;

+ 28 - 0
src/types/table/tourImGroupType.d.ts

@@ -0,0 +1,28 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourImGroupType {
+  // id
+  id?: number | undefined;
+  // 父级id
+  parentId?: number | undefined;
+  parentIdDictMap?: DictData,
+  // 类型名称
+  typeName?: string | undefined;
+  // 类型图标
+  typeIcon?: string | undefined;
+  // 是否启用 0否 1是
+  enable?: number | undefined;
+  enableDictMap?: DictData,
+  // 删除标记 1正常 -1删除
+  dataState?: number | undefined;
+  // 创建人id
+  createUserId?: number | undefined;
+  // 创建时间
+  createTime?: string | undefined;
+  // 更新时间
+  updateTime?: string | undefined;
+  // 更新人id
+  updateUserId?: number | undefined;
+}
+
+export default TourImGroupType;

+ 42 - 0
src/types/table/tourImMember.d.ts

@@ -0,0 +1,42 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourImMember {
+  // id
+  id?: number | undefined;
+  // 聊天群组id
+  groupId?: number | undefined;
+  groupIdDictMap?: DictData,
+  // 成员角色 1群主 2管理员 3普通成员
+  groupRole?: number | undefined;
+  groupRoleDictMap?: DictData,
+  // 用户id
+  userId?: number | undefined;
+  userIdDictMap?: DictData,
+  // 会话是否显示(每个群在聊天列表要不要展示) 0不显示 1显示
+  isShow?: number | undefined;
+  isShowDictMap?: DictData,
+  // 会话是否置顶 (每个群在聊天列表要不要置顶)  0不置顶 1 置顶
+  isTop?: number | undefined;
+  isTopDictMap?: DictData,
+  // 会话是否开启免打扰 0不开启免打扰 1 开启免打扰
+  isNotDisturb?: number | undefined;
+  isNotDisturbDictMap?: DictData,
+  // 自己在群里的昵称
+  groupNickname?: string | undefined;
+  // 群备注
+  groupRemark?: string | undefined;
+  // 删除标记 1正常 -1删除
+  dataState?: number | undefined;
+  // 聊天背景
+  groupBackImage?: string | undefined;
+  // 创建人id
+  createUserId?: number | undefined;
+  // 创建时间
+  createTime?: string | undefined;
+  // 更新时间
+  updateTime?: string | undefined;
+  // 更新人id
+  updateUserId?: number | undefined;
+}
+
+export default TourImMember;

+ 40 - 0
src/types/table/tourImMessage.d.ts

@@ -0,0 +1,40 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourImMessage {
+  // id
+  id?: number | undefined;
+  // 消息内容
+  messageContent?: string | undefined;
+  // 消息类型 0文字 1文件 2系统消息
+  messageType?: number | undefined;
+  messageTypeDictMap?: DictData,
+  // 聊天群组id
+  groupId?: number | undefined;
+  groupIdDictMap?: DictData,
+  // 通知消息类型 1.单聊消息 2 群聊消息 3系统消息 4关注信息 5点赞 6评论 7评论区艾特 8文章内艾特 9访问
+  noticeType?: number | undefined;
+  noticeTypeDictMap?: DictData,
+  // 消息置顶/公告 0不置顶 1置顶
+  isTop?: number | undefined;
+  isTopDictMap?: DictData,
+  // 撤回标记 0不撤回 1撤回
+  revocationTag?: number | undefined;
+  revocationTagDictMap?: DictData,
+  // 已经删除该条消息的用户
+  deletedByUsers?: string | undefined;
+  // 已读该条消息的用户
+  readByUsers?: string | undefined;
+  // 删除标记 1正常 -1删除
+  dataState?: number | undefined;
+  // 创建人id
+  createUserId?: number | undefined;
+  createUserIdDictMap?: DictData,
+  // 创建时间
+  createTime?: string | undefined;
+  // 更新时间
+  updateTime?: string | undefined;
+  // 更新人id
+  updateUserId?: number | undefined;
+}
+
+export default TourImMessage;

+ 10 - 0
src/types/table/tourImSensitiveWordAllow.d.ts

@@ -0,0 +1,10 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourImSensitiveWordAllow {
+  // id
+  id?: number | undefined;
+  // 白名单词汇
+  word?: string | undefined;
+}
+
+export default TourImSensitiveWordAllow;

+ 10 - 0
src/types/table/tourImSensitiveWordDeny.d.ts

@@ -0,0 +1,10 @@
+import { DictData } from '@/common/staticDict/types';
+
+interface TourImSensitiveWordDeny {
+  // id
+  id?: number | undefined;
+  // 黑名单词汇
+  word?: string | undefined;
+}
+
+export default TourImSensitiveWordDeny;