sa2kit 1.6.15 → 1.6.16

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