sa2kit 1.6.15 → 1.6.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,7 @@
1
1
  export { ExportConfigError, ExportDataError, ExportFileError, ExportServiceError } from '../chunk-7XLFSPDG.mjs';
2
2
  import '../chunk-BJTO5JO5.mjs';
3
+ import React, { useState, useCallback, useEffect } from 'react';
4
+ import { Settings, X, Eye, EyeOff, MoveUp, MoveDown, Trash2, Group, Save, Loader2, Download, ChevronDown, Type, Database, FileText, FileSpreadsheet } from 'lucide-react';
3
5
 
4
6
  // src/universalExport/constants.ts
5
7
  var UNIVERSAL_EXPORT_VERSION = "1.0.0";
@@ -547,7 +549,567 @@ var universalExportClient = new UniversalExportClient();
547
549
  function createExportClient(config) {
548
550
  return new UniversalExportClient(config);
549
551
  }
552
+ var FORMAT_ICONS = {
553
+ csv: /* @__PURE__ */ React.createElement(FileText, { className: "w-4 h-4" }),
554
+ excel: /* @__PURE__ */ React.createElement(FileSpreadsheet, { className: "w-4 h-4" }),
555
+ json: /* @__PURE__ */ React.createElement(Database, { className: "w-4 h-4" })
556
+ };
557
+ var FORMAT_DESCRIPTIONS = {
558
+ csv: "\u9017\u53F7\u5206\u9694\u503C\u6587\u4EF6\uFF0C\u517C\u5BB9\u6027\u6700\u597D",
559
+ excel: "Excel\u8868\u683C\u6587\u4EF6\uFF0C\u652F\u6301\u683C\u5F0F\u5316\u548C\u5355\u5143\u683C\u5408\u5E76",
560
+ json: "JSON\u6570\u636E\u6587\u4EF6\uFF0C\u9002\u5408\u5F00\u53D1\u8005\u4F7F\u7528"
561
+ };
562
+ var ExportConfigEditor = ({
563
+ initialConfig,
564
+ moduleId,
565
+ businessId,
566
+ availableFields,
567
+ onSave,
568
+ onCancel,
569
+ visible = false,
570
+ className = "",
571
+ onConfigChange
572
+ }) => {
573
+ const [config, setConfig] = useState(() => {
574
+ if (initialConfig) {
575
+ return { ...initialConfig };
576
+ }
577
+ return {
578
+ id: "",
579
+ name: "\u65B0\u5EFA\u5BFC\u51FA\u914D\u7F6E",
580
+ description: "",
581
+ format: "csv",
582
+ fields: availableFields.map((field, index) => ({
583
+ ...field,
584
+ enabled: true,
585
+ sortOrder: index
586
+ })),
587
+ grouping: {
588
+ enabled: false,
589
+ fields: [],
590
+ preserveOrder: true,
591
+ nullValueHandling: "separate",
592
+ nullGroupName: "\u672A\u5206\u7EC4"
593
+ },
594
+ fileNameTemplate: "\u5BFC\u51FA\u6570\u636E_{date}",
595
+ includeHeader: true,
596
+ delimiter: ",",
597
+ encoding: "utf-8",
598
+ addBOM: true,
599
+ maxRows: void 0,
600
+ createdAt: /* @__PURE__ */ new Date(),
601
+ updatedAt: /* @__PURE__ */ new Date(),
602
+ moduleId,
603
+ businessId
604
+ };
605
+ });
606
+ const [activeTab, setActiveTab] = useState("basic");
607
+ const [savedConfigs, setSavedConfigs] = useState([]);
608
+ const [loadingConfigs, setLoadingConfigs] = useState(false);
609
+ const [deletingConfigId, setDeletingConfigId] = useState(null);
610
+ const loadSavedConfigs = useCallback(async () => {
611
+ if (!visible || activeTab !== "manage") return;
612
+ setLoadingConfigs(true);
613
+ try {
614
+ const params = new URLSearchParams({ moduleId });
615
+ if (businessId) {
616
+ params.set("businessId", businessId);
617
+ }
618
+ const response = await fetch(`/api/universal-export/configs?${params.toString()}`);
619
+ if (response.ok) {
620
+ const data = await response.json();
621
+ setSavedConfigs(data.configs || []);
622
+ }
623
+ } catch (error) {
624
+ console.error("\u52A0\u8F7D\u914D\u7F6E\u5F02\u5E38:", error);
625
+ } finally {
626
+ setLoadingConfigs(false);
627
+ }
628
+ }, [visible, activeTab, moduleId, businessId]);
629
+ const deleteConfig = useCallback(async (configId) => {
630
+ setDeletingConfigId(configId);
631
+ try {
632
+ const response = await fetch(`/api/universal-export/configs/${configId}`, {
633
+ method: "DELETE"
634
+ });
635
+ if (response.ok) {
636
+ setSavedConfigs((prev) => prev.filter((cfg) => cfg.id !== configId));
637
+ onConfigChange?.();
638
+ }
639
+ } catch (error) {
640
+ console.error("\u5220\u9664\u914D\u7F6E\u5931\u8D25:", error);
641
+ } finally {
642
+ setDeletingConfigId(null);
643
+ }
644
+ }, [onConfigChange]);
645
+ const loadConfigToEditor = useCallback((config2) => {
646
+ setConfig(config2);
647
+ setActiveTab("basic");
648
+ }, []);
649
+ useEffect(() => {
650
+ if (activeTab === "manage") {
651
+ loadSavedConfigs();
652
+ }
653
+ }, [activeTab, loadSavedConfigs]);
654
+ useEffect(() => {
655
+ if (!visible) return;
656
+ const scrollY = window.scrollY;
657
+ document.body.style.position = "fixed";
658
+ document.body.style.top = `-${scrollY}px`;
659
+ document.body.style.left = "0";
660
+ document.body.style.right = "0";
661
+ document.body.style.overflow = "hidden";
662
+ const handleKeyDown = (e) => {
663
+ if (e.key === "Escape") {
664
+ onCancel?.();
665
+ }
666
+ };
667
+ document.addEventListener("keydown", handleKeyDown);
668
+ return () => {
669
+ document.body.style.position = "";
670
+ document.body.style.top = "";
671
+ document.body.style.left = "";
672
+ document.body.style.right = "";
673
+ document.body.style.overflow = "";
674
+ window.scrollTo(0, scrollY);
675
+ document.removeEventListener("keydown", handleKeyDown);
676
+ };
677
+ }, [visible, onCancel]);
678
+ const toggleFieldEnabled = useCallback((fieldKey) => {
679
+ setConfig((prev) => ({
680
+ ...prev,
681
+ fields: prev.fields.map(
682
+ (field) => field.key === fieldKey ? { ...field, enabled: !field.enabled } : field
683
+ )
684
+ }));
685
+ }, []);
686
+ useCallback((fieldKey, updates) => {
687
+ setConfig((prev) => ({
688
+ ...prev,
689
+ fields: prev.fields.map(
690
+ (field) => field.key === fieldKey ? { ...field, ...updates } : field
691
+ )
692
+ }));
693
+ }, []);
694
+ const moveField = useCallback((fieldKey, direction) => {
695
+ setConfig((prev) => {
696
+ const fields = [...prev.fields];
697
+ const index = fields.findIndex((f) => f.key === fieldKey);
698
+ if (direction === "up" && index > 0) {
699
+ const temp = fields[index - 1];
700
+ fields[index - 1] = fields[index];
701
+ fields[index] = temp;
702
+ } else if (direction === "down" && index < fields.length - 1) {
703
+ const temp = fields[index + 1];
704
+ fields[index + 1] = fields[index];
705
+ fields[index] = temp;
706
+ }
707
+ return {
708
+ ...prev,
709
+ fields: fields.map((field, idx) => ({ ...field, sortOrder: idx }))
710
+ };
711
+ });
712
+ }, []);
713
+ const removeField = useCallback((fieldKey) => {
714
+ setConfig((prev) => ({
715
+ ...prev,
716
+ fields: prev.fields.filter((f) => f.key !== fieldKey)
717
+ }));
718
+ }, []);
719
+ const toggleGrouping = useCallback((enabled) => {
720
+ setConfig((prev) => ({
721
+ ...prev,
722
+ grouping: {
723
+ ...prev.grouping,
724
+ enabled
725
+ }
726
+ }));
727
+ }, []);
728
+ const addGroupingField = useCallback((fieldKey) => {
729
+ const field = config.fields.find((f) => f.key === fieldKey);
730
+ if (!field) return;
731
+ const groupField = {
732
+ key: field.key,
733
+ label: field.label,
734
+ mode: "merge",
735
+ valueProcessing: "first",
736
+ showGroupHeader: false,
737
+ mergeCells: true
738
+ };
739
+ setConfig((prev) => ({
740
+ ...prev,
741
+ grouping: {
742
+ ...prev.grouping,
743
+ fields: [...prev.grouping.fields, groupField]
744
+ }
745
+ }));
746
+ }, [config.fields]);
747
+ const removeGroupingField = useCallback((fieldKey) => {
748
+ setConfig((prev) => ({
749
+ ...prev,
750
+ grouping: {
751
+ ...prev.grouping,
752
+ fields: prev.grouping.fields.filter((f) => f.key !== fieldKey)
753
+ }
754
+ }));
755
+ }, []);
756
+ const updateGroupingField = useCallback((fieldKey, updates) => {
757
+ setConfig((prev) => ({
758
+ ...prev,
759
+ grouping: {
760
+ ...prev.grouping,
761
+ fields: prev.grouping.fields.map(
762
+ (field) => field.key === fieldKey ? { ...field, ...updates } : field
763
+ )
764
+ }
765
+ }));
766
+ }, []);
767
+ useCallback((fieldKey, direction) => {
768
+ setConfig((prev) => {
769
+ const fields = [...prev.grouping.fields];
770
+ const index = fields.findIndex((f) => f.key === fieldKey);
771
+ if (direction === "up" && index > 0) {
772
+ const temp = fields[index - 1];
773
+ fields[index - 1] = fields[index];
774
+ fields[index] = temp;
775
+ } else if (direction === "down" && index < fields.length - 1) {
776
+ const temp = fields[index + 1];
777
+ fields[index + 1] = fields[index];
778
+ fields[index] = temp;
779
+ }
780
+ return {
781
+ ...prev,
782
+ grouping: {
783
+ ...prev.grouping,
784
+ fields
785
+ }
786
+ };
787
+ });
788
+ }, []);
789
+ useCallback((updates) => {
790
+ setConfig((prev) => ({
791
+ ...prev,
792
+ grouping: {
793
+ ...prev.grouping,
794
+ ...updates
795
+ }
796
+ }));
797
+ }, []);
798
+ const handleSave = useCallback(() => {
799
+ if (!config.name.trim()) {
800
+ alert("\u8BF7\u8F93\u5165\u914D\u7F6E\u540D\u79F0");
801
+ return;
802
+ }
803
+ const enabledFields = config.fields.filter((f) => f.enabled);
804
+ if (enabledFields.length === 0) {
805
+ alert("\u81F3\u5C11\u9700\u8981\u542F\u7528\u4E00\u4E2A\u5B57\u6BB5");
806
+ return;
807
+ }
808
+ const updatedConfig = {
809
+ ...config,
810
+ updatedAt: /* @__PURE__ */ new Date()
811
+ };
812
+ onSave?.(updatedConfig);
813
+ onConfigChange?.();
814
+ }, [config, onSave, onConfigChange]);
815
+ if (!visible) {
816
+ return null;
817
+ }
818
+ const tabs = [
819
+ { id: "basic", label: "\u57FA\u672C\u914D\u7F6E", icon: /* @__PURE__ */ React.createElement(Settings, { className: "w-4 h-4" }), description: "\u914D\u7F6E\u540D\u79F0\u3001\u683C\u5F0F\u548C\u57FA\u672C\u9009\u9879" },
820
+ { id: "fields", label: "\u5B57\u6BB5\u8BBE\u7F6E", icon: /* @__PURE__ */ React.createElement(Type, { className: "w-4 h-4" }), description: "\u9009\u62E9\u548C\u914D\u7F6E\u5BFC\u51FA\u5B57\u6BB5" },
821
+ { id: "grouping", label: "\u5206\u7EC4\u8BBE\u7F6E", icon: /* @__PURE__ */ React.createElement(Group, { className: "w-4 h-4" }), description: "\u914D\u7F6E\u6570\u636E\u5206\u7EC4\u548C\u5408\u5E76\u9009\u9879" },
822
+ { id: "manage", label: "\u914D\u7F6E\u7BA1\u7406", icon: /* @__PURE__ */ React.createElement(Database, { className: "w-4 h-4" }), description: "\u7BA1\u7406\u5DF2\u4FDD\u5B58\u7684\u5BFC\u51FA\u914D\u7F6E" }
823
+ ];
824
+ return /* @__PURE__ */ React.createElement(
825
+ "div",
826
+ {
827
+ className: `fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4 ${className}`,
828
+ onClick: onCancel
829
+ },
830
+ /* @__PURE__ */ React.createElement(
831
+ "div",
832
+ {
833
+ className: "bg-white rounded-lg shadow-xl w-full max-w-5xl max-h-[90vh] flex flex-col",
834
+ onClick: (e) => e.stopPropagation()
835
+ },
836
+ /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between p-4 border-b flex-shrink-0" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React.createElement(Settings, { className: "w-6 h-6 text-blue-600" }), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-xl font-semibold text-gray-900" }, "\u5BFC\u51FA\u914D\u7F6E\u7F16\u8F91\u5668"), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-600" }, "\u914D\u7F6E\u5BFC\u51FA\u5B57\u6BB5\u548C\u683C\u5F0F\u9009\u9879"))), /* @__PURE__ */ React.createElement("button", { onClick: onCancel, className: "p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg" }, /* @__PURE__ */ React.createElement(X, { className: "w-5 h-5" }))),
837
+ /* @__PURE__ */ React.createElement("div", { className: "border-b bg-gray-50 flex-shrink-0" }, /* @__PURE__ */ React.createElement("nav", { className: "flex space-x-1 p-2" }, tabs.map((tab) => /* @__PURE__ */ React.createElement(
838
+ "button",
839
+ {
840
+ key: tab.id,
841
+ onClick: () => setActiveTab(tab.id),
842
+ className: `flex items-center gap-2 px-4 py-3 text-sm font-medium rounded-lg transition-colors ${activeTab === tab.id ? "bg-white text-blue-600 shadow-sm border border-gray-200" : "text-gray-600 hover:text-gray-900"}`
843
+ },
844
+ tab.icon,
845
+ /* @__PURE__ */ React.createElement("span", null, tab.label)
846
+ )))),
847
+ /* @__PURE__ */ React.createElement("div", { className: "flex-1 overflow-hidden flex flex-col" }, /* @__PURE__ */ React.createElement("div", { className: "bg-blue-50 border-b border-blue-100 p-4 flex-shrink-0" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 text-blue-800 text-sm font-medium" }, tabs.find((t) => t.id === activeTab)?.icon, tabs.find((t) => t.id === activeTab)?.description)), /* @__PURE__ */ React.createElement("div", { className: "flex-1 overflow-y-auto p-4 sm:p-6" }, activeTab === "basic" && /* @__PURE__ */ React.createElement("div", { className: "max-w-2xl mx-auto space-y-6" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React.createElement("label", { className: "block text-sm font-medium text-gray-700" }, "\u914D\u7F6E\u540D\u79F0 *"), /* @__PURE__ */ React.createElement(
848
+ "input",
849
+ {
850
+ type: "text",
851
+ value: config.name,
852
+ onChange: (e) => setConfig((prev) => ({ ...prev, name: e.target.value })),
853
+ className: "w-full px-3 py-2 border border-gray-300 rounded-lg"
854
+ }
855
+ ), /* @__PURE__ */ React.createElement("label", { className: "block text-sm font-medium text-gray-700" }, "\u63CF\u8FF0"), /* @__PURE__ */ React.createElement(
856
+ "textarea",
857
+ {
858
+ value: config.description,
859
+ onChange: (e) => setConfig((prev) => ({ ...prev, description: e.target.value })),
860
+ className: "w-full px-3 py-2 border border-gray-300 rounded-lg",
861
+ rows: 3
862
+ }
863
+ ), /* @__PURE__ */ React.createElement("label", { className: "block text-sm font-medium text-gray-700" }, "\u5BFC\u51FA\u683C\u5F0F"), /* @__PURE__ */ React.createElement("div", { className: "space-y-3" }, ["csv", "excel", "json"].map((format) => /* @__PURE__ */ React.createElement("label", { key: format, className: "flex items-start gap-3 p-3 border rounded-lg cursor-pointer hover:border-blue-300" }, /* @__PURE__ */ React.createElement(
864
+ "input",
865
+ {
866
+ type: "radio",
867
+ checked: config.format === format,
868
+ onChange: () => setConfig((prev) => ({ ...prev, format })),
869
+ className: "mt-1"
870
+ }
871
+ ), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 font-medium" }, FORMAT_ICONS[format], " ", format.toUpperCase()), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-600" }, FORMAT_DESCRIPTIONS[format]))))))), activeTab === "fields" && /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, config.fields.map((field, index) => /* @__PURE__ */ React.createElement("div", { key: field.key, className: "flex items-center gap-3 p-3 border rounded-lg bg-white" }, /* @__PURE__ */ React.createElement("button", { onClick: () => toggleFieldEnabled(field.key), className: field.enabled ? "text-blue-600" : "text-gray-400" }, field.enabled ? /* @__PURE__ */ React.createElement(Eye, { className: "w-4 h-4" }) : /* @__PURE__ */ React.createElement(EyeOff, { className: "w-4 h-4" })), /* @__PURE__ */ React.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React.createElement("div", { className: "font-medium" }, field.label), /* @__PURE__ */ React.createElement("div", { className: "text-xs text-gray-500" }, field.key)), /* @__PURE__ */ React.createElement("div", { className: "flex gap-1" }, /* @__PURE__ */ React.createElement("button", { onClick: () => moveField(field.key, "up"), disabled: index === 0, className: "disabled:opacity-30" }, /* @__PURE__ */ React.createElement(MoveUp, { className: "w-4 h-4" })), /* @__PURE__ */ React.createElement("button", { onClick: () => moveField(field.key, "down"), disabled: index === config.fields.length - 1, className: "disabled:opacity-30" }, /* @__PURE__ */ React.createElement(MoveDown, { className: "w-4 h-4" })), /* @__PURE__ */ React.createElement("button", { onClick: () => removeField(field.key), className: "text-red-600" }, /* @__PURE__ */ React.createElement(Trash2, { className: "w-4 h-4" })))))), activeTab === "grouping" && /* @__PURE__ */ React.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-medium flex items-center gap-2" }, /* @__PURE__ */ React.createElement(Group, { className: "w-5 h-5 text-blue-600" }), " \u5206\u7EC4\u914D\u7F6E"), /* @__PURE__ */ React.createElement("label", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", checked: config.grouping?.enabled, onChange: (e) => toggleGrouping(e.target.checked) }), "\u542F\u7528\u5206\u7EC4")), config.grouping?.enabled && /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, config.grouping.fields.map((gf, index) => /* @__PURE__ */ React.createElement("div", { key: gf.key, className: "p-4 border rounded-lg bg-blue-50 border-blue-200" }, /* @__PURE__ */ React.createElement("div", { className: "flex justify-between items-center mb-4" }, /* @__PURE__ */ React.createElement("span", { className: "font-medium text-blue-900" }, gf.label), /* @__PURE__ */ React.createElement("button", { onClick: () => removeGroupingField(gf.key), className: "text-red-600" }, /* @__PURE__ */ React.createElement(Trash2, { className: "w-4 h-4" }))), /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React.createElement("select", { value: gf.mode, onChange: (e) => updateGroupingField(gf.key, { mode: e.target.value }), className: "border rounded p-2" }, /* @__PURE__ */ React.createElement("option", { value: "merge" }, "\u5408\u5E76\u6A21\u5F0F"), /* @__PURE__ */ React.createElement("option", { value: "separate" }, "\u5206\u79BB\u6A21\u5F0F"), /* @__PURE__ */ React.createElement("option", { value: "nested" }, "\u5D4C\u5957\u6A21\u5F0F")), /* @__PURE__ */ React.createElement("select", { value: gf.valueProcessing, onChange: (e) => updateGroupingField(gf.key, { valueProcessing: e.target.value }), className: "border rounded p-2" }, /* @__PURE__ */ React.createElement("option", { value: "first" }, "\u9996\u503C"), /* @__PURE__ */ React.createElement("option", { value: "sum" }, "\u6C42\u548C"), /* @__PURE__ */ React.createElement("option", { value: "count" }, "\u8BA1\u6570"))))), /* @__PURE__ */ React.createElement("select", { onChange: (e) => e.target.value && addGroupingField(e.target.value), className: "w-full border rounded p-2" }, /* @__PURE__ */ React.createElement("option", { value: "" }, "\u6DFB\u52A0\u5206\u7EC4\u5B57\u6BB5..."), config.fields.filter((f) => f.enabled && !config.grouping?.fields.some((gf) => gf.key === f.key)).map((f) => /* @__PURE__ */ React.createElement("option", { key: f.key, value: f.key }, f.label))))), activeTab === "manage" && /* @__PURE__ */ React.createElement("div", { className: "space-y-4" }, loadingConfigs ? /* @__PURE__ */ React.createElement("div", { className: "text-center py-10" }, "\u52A0\u8F7D\u4E2D...") : savedConfigs.map((sc) => /* @__PURE__ */ React.createElement("div", { key: sc.id, className: "flex justify-between items-center p-4 border rounded-lg" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "font-medium" }, sc.name), /* @__PURE__ */ React.createElement("div", { className: "text-sm text-gray-500" }, sc.format)), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React.createElement("button", { onClick: () => loadConfigToEditor(sc), className: "text-blue-600 px-3 py-1 border rounded" }, "\u7F16\u8F91"), /* @__PURE__ */ React.createElement("button", { onClick: () => deleteConfig(sc.id), className: "text-red-600 px-3 py-1 border rounded" }, "\u5220\u9664"))))))),
872
+ /* @__PURE__ */ React.createElement("div", { className: "flex justify-end gap-3 p-4 border-t bg-gray-50" }, /* @__PURE__ */ React.createElement("button", { onClick: onCancel, className: "px-4 py-2 border rounded" }, "\u53D6\u6D88"), activeTab !== "manage" && /* @__PURE__ */ React.createElement("button", { onClick: handleSave, className: "px-4 py-2 bg-blue-600 text-white rounded flex items-center gap-2" }, /* @__PURE__ */ React.createElement(Save, { className: "w-4 h-4" }), " \u4FDD\u5B58\u914D\u7F6E"))
873
+ )
874
+ );
875
+ };
876
+
877
+ // src/universalExport/components/UniversalExportButton.tsx
878
+ var BUTTON_STYLES = {
879
+ primary: {
880
+ sm: "px-3 py-1.5 text-sm bg-blue-600 text-white hover:bg-blue-700",
881
+ md: "px-4 py-2 text-sm bg-blue-600 text-white hover:bg-blue-700",
882
+ lg: "px-6 py-3 text-base bg-blue-600 text-white hover:bg-blue-700"
883
+ },
884
+ secondary: {
885
+ sm: "px-3 py-1.5 text-sm bg-gray-600 text-white hover:bg-gray-700",
886
+ md: "px-4 py-2 text-sm bg-gray-600 text-white hover:bg-gray-700",
887
+ lg: "px-6 py-3 text-base bg-gray-600 text-white hover:bg-gray-700"
888
+ },
889
+ outline: {
890
+ sm: "px-3 py-1.5 text-sm border border-gray-300 text-gray-700 hover:bg-gray-50",
891
+ md: "px-4 py-2 text-sm border border-gray-300 text-gray-700 hover:bg-gray-50",
892
+ lg: "px-6 py-3 text-base border border-gray-300 text-gray-700 hover:bg-gray-50"
893
+ }
894
+ };
895
+ var UniversalExportButton = ({
896
+ exportService,
897
+ moduleId,
898
+ businessId,
899
+ availableFields,
900
+ dataSource,
901
+ defaultConfig,
902
+ buttonText = "\u5BFC\u51FA\u6570\u636E",
903
+ variant = "primary",
904
+ size = "md",
905
+ disabled = false,
906
+ className = "",
907
+ onExportSuccess,
908
+ onExportError,
909
+ onConfigSave
910
+ }) => {
911
+ const [showConfigEditor, setShowConfigEditor] = useState(false);
912
+ const [showDropdown, setShowDropdown] = useState(false);
913
+ const [isExporting, setIsExporting] = useState(false);
914
+ const [exportProgress, setExportProgress] = useState(null);
915
+ const [savedConfigs, setSavedConfigs] = useState([]);
916
+ const loadSavedConfigs = useCallback(async () => {
917
+ try {
918
+ if (exportService) {
919
+ const configs = await exportService.getConfigsByModule(moduleId, businessId);
920
+ setSavedConfigs(configs);
921
+ }
922
+ } catch (error) {
923
+ console.error("\u52A0\u8F7D\u4FDD\u5B58\u7684\u914D\u7F6E\u5931\u8D25:", error);
924
+ }
925
+ }, [exportService, moduleId, businessId]);
926
+ React.useEffect(() => {
927
+ loadSavedConfigs();
928
+ }, [loadSavedConfigs]);
929
+ const handleExport = useCallback(async (config) => {
930
+ console.log("\u{1F680} [UniversalExportButton] \u5F00\u59CB\u5BFC\u51FA:", {
931
+ configId: config.id,
932
+ configName: config.name,
933
+ format: config.format,
934
+ fieldsCount: config.fields.length
935
+ });
936
+ if (!exportService) {
937
+ console.error("\u274C [UniversalExportButton] \u5BFC\u51FA\u670D\u52A1\u672A\u521D\u59CB\u5316");
938
+ onExportError?.("\u5BFC\u51FA\u670D\u52A1\u672A\u521D\u59CB\u5316");
939
+ return;
940
+ }
941
+ setIsExporting(true);
942
+ setExportProgress(null);
943
+ try {
944
+ const request = {
945
+ configId: config,
946
+ dataSource,
947
+ callbacks: {
948
+ onProgress: (progress) => {
949
+ console.log("\u{1F4CA} [UniversalExportButton] \u5BFC\u51FA\u8FDB\u5EA6:", progress);
950
+ setExportProgress(progress);
951
+ },
952
+ onSuccess: (result) => {
953
+ console.log("\u2705 [UniversalExportButton] \u5BFC\u51FA\u6210\u529F:", {
954
+ fileName: result.fileName,
955
+ fileSize: result.fileSize,
956
+ exportedRows: result.exportedRows
957
+ });
958
+ setIsExporting(false);
959
+ setExportProgress(null);
960
+ if (result.fileBlob) {
961
+ console.log("\u{1F4E5} [UniversalExportButton] \u5F00\u59CB\u4E0B\u8F7D\u6587\u4EF6...");
962
+ const url = window.URL.createObjectURL(result.fileBlob);
963
+ const link = document.createElement("a");
964
+ link.href = url;
965
+ link.download = result.fileName;
966
+ document.body.appendChild(link);
967
+ link.click();
968
+ document.body.removeChild(link);
969
+ window.URL.revokeObjectURL(url);
970
+ console.log("\u2705 [UniversalExportButton] \u6587\u4EF6\u4E0B\u8F7D\u5B8C\u6210");
971
+ }
972
+ onExportSuccess?.(result);
973
+ },
974
+ onError: (error) => {
975
+ console.error("\u274C [UniversalExportButton] \u5BFC\u51FA\u5931\u8D25:", error);
976
+ setIsExporting(false);
977
+ setExportProgress(null);
978
+ onExportError?.(error.message);
979
+ }
980
+ }
981
+ };
982
+ console.log("\u{1F4DE} [UniversalExportButton] \u8C03\u7528\u5BFC\u51FA\u670D\u52A1...");
983
+ await exportService.exportData(request);
984
+ } catch (error) {
985
+ console.error("\u274C [UniversalExportButton] \u5BFC\u51FA\u5F02\u5E38:", error);
986
+ setIsExporting(false);
987
+ setExportProgress(null);
988
+ onExportError?.(error instanceof Error ? error.message : "\u5BFC\u51FA\u5931\u8D25");
989
+ }
990
+ }, [exportService, dataSource, onExportSuccess, onExportError]);
991
+ const handleQuickExport = useCallback(async () => {
992
+ if (defaultConfig) {
993
+ await handleExport(defaultConfig);
994
+ } else {
995
+ const config = {
996
+ id: "quick_export",
997
+ name: "\u5FEB\u901F\u5BFC\u51FA",
998
+ description: "\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E\u5FEB\u901F\u5BFC\u51FA",
999
+ format: "csv",
1000
+ fields: availableFields.map((field, index) => ({
1001
+ ...field,
1002
+ enabled: true,
1003
+ sortOrder: index
1004
+ })),
1005
+ fileNameTemplate: "\u5BFC\u51FA\u6570\u636E_{date}",
1006
+ includeHeader: true,
1007
+ delimiter: ",",
1008
+ encoding: "utf-8",
1009
+ addBOM: true,
1010
+ createdAt: /* @__PURE__ */ new Date(),
1011
+ updatedAt: /* @__PURE__ */ new Date(),
1012
+ moduleId,
1013
+ businessId
1014
+ };
1015
+ await handleExport(config);
1016
+ }
1017
+ }, [defaultConfig, availableFields, moduleId, businessId, handleExport]);
1018
+ const handleConfigSave = useCallback(async (config) => {
1019
+ try {
1020
+ if (exportService) {
1021
+ const savedConfig = await exportService.createConfig(config);
1022
+ await loadSavedConfigs();
1023
+ onConfigSave?.(savedConfig);
1024
+ }
1025
+ setShowConfigEditor(false);
1026
+ } catch (error) {
1027
+ onExportError?.(error instanceof Error ? error.message : "\u4FDD\u5B58\u914D\u7F6E\u5931\u8D25");
1028
+ }
1029
+ }, [exportService, onConfigSave, onExportError, loadSavedConfigs]);
1030
+ const renderProgress = () => {
1031
+ if (!exportProgress) return null;
1032
+ const { status, progress, processedRows, totalRows } = exportProgress;
1033
+ return /* @__PURE__ */ React.createElement("div", { className: "absolute top-full left-0 right-0 mt-2 bg-white border border-gray-200 rounded-lg shadow-lg p-4 z-10" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-3 mb-2" }, /* @__PURE__ */ React.createElement(Loader2, { className: "w-4 h-4 animate-spin text-blue-600" }), /* @__PURE__ */ React.createElement("span", { className: "text-sm font-medium text-gray-900" }, status === "processing" ? "\u6B63\u5728\u5BFC\u51FA..." : "\u5BFC\u51FA\u5B8C\u6210")), /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex justify-between text-xs text-gray-600" }, /* @__PURE__ */ React.createElement("span", null, "\u8FDB\u5EA6: ", progress, "%"), /* @__PURE__ */ React.createElement("span", null, processedRows, " / ", totalRows, " \u884C")), /* @__PURE__ */ React.createElement("div", { className: "w-full bg-gray-200 rounded-full h-2" }, /* @__PURE__ */ React.createElement(
1034
+ "div",
1035
+ {
1036
+ className: "bg-blue-600 h-2 rounded-full transition-all duration-300",
1037
+ style: { width: `${progress}%` }
1038
+ }
1039
+ ))));
1040
+ };
1041
+ const renderDropdown = () => {
1042
+ if (!showDropdown) return null;
1043
+ return /* @__PURE__ */ React.createElement("div", { className: "absolute top-full left-0 mt-2 bg-white border border-gray-200 rounded-lg shadow-lg py-2 z-10 min-w-48" }, /* @__PURE__ */ React.createElement(
1044
+ "button",
1045
+ {
1046
+ onClick: () => {
1047
+ setShowDropdown(false);
1048
+ handleQuickExport();
1049
+ },
1050
+ disabled: isExporting,
1051
+ className: "w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
1052
+ },
1053
+ /* @__PURE__ */ React.createElement(Download, { className: "w-4 h-4" }),
1054
+ "\u5FEB\u901F\u5BFC\u51FA"
1055
+ ), savedConfigs.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "border-t border-gray-200 my-1" }), savedConfigs.map((config) => /* @__PURE__ */ React.createElement(
1056
+ "button",
1057
+ {
1058
+ key: config.id,
1059
+ onClick: () => {
1060
+ setShowDropdown(false);
1061
+ handleExport(config);
1062
+ },
1063
+ disabled: isExporting,
1064
+ className: "w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
1065
+ },
1066
+ /* @__PURE__ */ React.createElement(FileText, { className: "w-4 h-4" }),
1067
+ config.name
1068
+ ))), /* @__PURE__ */ React.createElement("div", { className: "border-t border-gray-200 my-1" }), /* @__PURE__ */ React.createElement(
1069
+ "button",
1070
+ {
1071
+ onClick: () => {
1072
+ setShowDropdown(false);
1073
+ setShowConfigEditor(true);
1074
+ },
1075
+ className: "w-full px-4 py-2 text-left text-sm text-gray-700 hover:bg-gray-50 flex items-center gap-2"
1076
+ },
1077
+ /* @__PURE__ */ React.createElement(Settings, { className: "w-4 h-4" }),
1078
+ "\u81EA\u5B9A\u4E49\u914D\u7F6E"
1079
+ ));
1080
+ };
1081
+ const buttonStyle = BUTTON_STYLES[variant][size];
1082
+ const baseClasses = `inline-flex items-center gap-2 rounded-lg font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed ${buttonStyle}`;
1083
+ return /* @__PURE__ */ React.createElement("div", { className: `relative ${className}` }, /* @__PURE__ */ React.createElement(
1084
+ "button",
1085
+ {
1086
+ onClick: () => setShowDropdown(!showDropdown),
1087
+ disabled: disabled || isExporting,
1088
+ className: `${baseClasses} ${isExporting ? "cursor-not-allowed" : ""}`
1089
+ },
1090
+ isExporting ? /* @__PURE__ */ React.createElement(Loader2, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ React.createElement(Download, { className: "w-4 h-4" }),
1091
+ /* @__PURE__ */ React.createElement("span", null, isExporting ? "\u5BFC\u51FA\u4E2D..." : buttonText),
1092
+ /* @__PURE__ */ React.createElement(ChevronDown, { className: "w-4 h-4" })
1093
+ ), renderDropdown(), renderProgress(), /* @__PURE__ */ React.createElement(
1094
+ ExportConfigEditor,
1095
+ {
1096
+ moduleId,
1097
+ businessId,
1098
+ availableFields,
1099
+ onSave: handleConfigSave,
1100
+ onCancel: () => setShowConfigEditor(false),
1101
+ visible: showConfigEditor,
1102
+ onConfigChange: loadSavedConfigs
1103
+ }
1104
+ ), showDropdown && /* @__PURE__ */ React.createElement(
1105
+ "div",
1106
+ {
1107
+ className: "fixed inset-0 z-0",
1108
+ onClick: () => setShowDropdown(false)
1109
+ }
1110
+ ));
1111
+ };
550
1112
 
551
- export { API_BASE_PATH, API_ENDPOINTS, DEFAULT_ADD_BOM, DEFAULT_CONFIG_CACHE_TTL, DEFAULT_CSV_DELIMITER, DEFAULT_ENCODING, DEFAULT_EXPORT_FORMAT, DEFAULT_EXPORT_TIMEOUT, DEFAULT_FORMATTERS, DEFAULT_MAX_CONCURRENT_EXPORTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_ROWS, DEFAULT_RESULT_CACHE_TTL, ERROR_CODES, EXPORT_FORMAT_EXTENSIONS, EXPORT_FORMAT_MIME_TYPES, UNIVERSAL_EXPORT_NAME, UNIVERSAL_EXPORT_VERSION, UniversalExportClient, applyFormatter, createExportClient, createExportError, escapeCSVField, estimateEndTime, formatDuration, formatErrorMessage, formatFileSize, generateExportFileName, getNestedValue, parseCSVField, sanitizeFileName, setNestedValue, universalExportClient, validateExportConfig, validateExportRequest, validateFileName };
1113
+ export { API_BASE_PATH, API_ENDPOINTS, DEFAULT_ADD_BOM, DEFAULT_CONFIG_CACHE_TTL, DEFAULT_CSV_DELIMITER, DEFAULT_ENCODING, DEFAULT_EXPORT_FORMAT, DEFAULT_EXPORT_TIMEOUT, DEFAULT_FORMATTERS, DEFAULT_MAX_CONCURRENT_EXPORTS, DEFAULT_MAX_FILE_SIZE, DEFAULT_MAX_ROWS, DEFAULT_RESULT_CACHE_TTL, ERROR_CODES, EXPORT_FORMAT_EXTENSIONS, EXPORT_FORMAT_MIME_TYPES, ExportConfigEditor, UNIVERSAL_EXPORT_NAME, UNIVERSAL_EXPORT_VERSION, UniversalExportButton, UniversalExportClient, applyFormatter, createExportClient, createExportError, escapeCSVField, estimateEndTime, formatDuration, formatErrorMessage, formatFileSize, generateExportFileName, getNestedValue, parseCSVField, sanitizeFileName, setNestedValue, universalExportClient, validateExportConfig, validateExportRequest, validateFileName };
552
1114
  //# sourceMappingURL=index.mjs.map
553
1115
  //# sourceMappingURL=index.mjs.map