@toototech/webbuilder-plugins 0.1.0

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.
Files changed (193) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +3 -0
  3. package/dist/basic/cssScope.d.ts +3 -0
  4. package/dist/basic/index.d.ts +81 -0
  5. package/dist/basic/injectStyle.d.ts +20 -0
  6. package/dist/basic/plugin.d.ts +19 -0
  7. package/dist/basic/publisher.d.ts +4 -0
  8. package/dist/basic/registries/interactive/accordion/index.d.ts +3 -0
  9. package/dist/basic/registries/interactive/backButton/index.d.ts +3 -0
  10. package/dist/basic/registries/interactive/button/index.d.ts +10 -0
  11. package/dist/basic/registries/interactive/countUp/index.d.ts +9 -0
  12. package/dist/basic/registries/interactive/customCode/index.d.ts +3 -0
  13. package/dist/basic/registries/interactive/index.d.ts +2 -0
  14. package/dist/basic/registries/interactive/inquiryForm/index.d.ts +3 -0
  15. package/dist/basic/registries/interactive/klaviyoSubscribe/index.d.ts +3 -0
  16. package/dist/basic/registries/interactive/popup/index.d.ts +5 -0
  17. package/dist/basic/registries/interactive/salesmartlyChatButton/index.d.ts +10 -0
  18. package/dist/basic/registries/interactive/search/index.d.ts +10 -0
  19. package/dist/basic/registries/interactive/socialShare/index.d.ts +33 -0
  20. package/dist/basic/registries/interactive/tabs/index.d.ts +3 -0
  21. package/dist/basic/registries/layout/container/index.d.ts +19 -0
  22. package/dist/basic/registries/layout/divider/index.d.ts +7 -0
  23. package/dist/basic/registries/layout/grid/index.d.ts +11 -0
  24. package/dist/basic/registries/layout/index.d.ts +2 -0
  25. package/dist/basic/registries/layout/layoutBase/index.d.ts +10 -0
  26. package/dist/basic/registries/layout/section/index.d.ts +3 -0
  27. package/dist/basic/registries/layout/sectionGridBlock/index.d.ts +3 -0
  28. package/dist/basic/registries/layout/spacer/index.d.ts +7 -0
  29. package/dist/basic/registries/media/banner/index.d.ts +11 -0
  30. package/dist/basic/registries/media/carousel/index.d.ts +6 -0
  31. package/dist/basic/registries/media/flipbook/index.d.ts +5 -0
  32. package/dist/basic/registries/media/icon/index.d.ts +3 -0
  33. package/dist/basic/registries/media/image/index.d.ts +3 -0
  34. package/dist/basic/registries/media/index.d.ts +2 -0
  35. package/dist/basic/registries/media/industryTabs/index.d.ts +3 -0
  36. package/dist/basic/registries/media/marquee/index.d.ts +4 -0
  37. package/dist/basic/registries/media/ourSolutions/helpers.d.ts +289 -0
  38. package/dist/basic/registries/media/ourSolutions/index.d.ts +4 -0
  39. package/dist/basic/registries/media/ourSolutions/script.d.ts +1 -0
  40. package/dist/basic/registries/media/ourSolutions/style.d.ts +1 -0
  41. package/dist/basic/registries/media/pdfViewer/index.d.ts +3 -0
  42. package/dist/basic/registries/media/productCategories/index.d.ts +4 -0
  43. package/dist/basic/registries/media/tabMediaGallery/index.d.ts +3 -0
  44. package/dist/basic/registries/media/video/index.d.ts +12 -0
  45. package/dist/basic/registries/navigation/footer/index.d.ts +10 -0
  46. package/dist/basic/registries/navigation/index.d.ts +2 -0
  47. package/dist/basic/registries/navigation/languageSwitcher/index.d.ts +6 -0
  48. package/dist/basic/registries/navigation/logo/index.d.ts +9 -0
  49. package/dist/basic/registries/navigation/navbar/constants.d.ts +38 -0
  50. package/dist/basic/registries/navigation/navbar/factories.d.ts +848 -0
  51. package/dist/basic/registries/navigation/navbar/helpers.d.ts +12 -0
  52. package/dist/basic/registries/navigation/navbar/index.d.ts +4 -0
  53. package/dist/basic/registries/navigation/navbar/registerMegaTypes.d.ts +2 -0
  54. package/dist/basic/registries/navigation/navbar/registerMenuTypes.d.ts +2 -0
  55. package/dist/basic/registries/navigation/navbar/registerRootTypes.d.ts +2 -0
  56. package/dist/basic/registries/navigation/navbar/script.d.ts +1 -0
  57. package/dist/basic/registries/navigation/navbar/style.d.ts +1 -0
  58. package/dist/basic/registries/navigation/socialLinks/index.d.ts +3 -0
  59. package/dist/basic/registries/section/allApplications/index.d.ts +3 -0
  60. package/dist/basic/registries/section/cardCarouselSection/index.d.ts +3 -0
  61. package/dist/basic/registries/section/caseSpotlight/index.d.ts +3 -0
  62. package/dist/basic/registries/section/companyScale/index.d.ts +3 -0
  63. package/dist/basic/registries/section/customizationGrid/index.d.ts +3 -0
  64. package/dist/basic/registries/section/factoryMap/index.d.ts +3 -0
  65. package/dist/basic/registries/section/focaHistoryTimeline/index.d.ts +3 -0
  66. package/dist/basic/registries/section/historyTimeline/index.d.ts +3 -0
  67. package/dist/basic/registries/section/homeBannerCarousel/index.d.ts +4 -0
  68. package/dist/basic/registries/section/hotspotShowcase/index.d.ts +30 -0
  69. package/dist/basic/registries/section/index.d.ts +2 -0
  70. package/dist/basic/registries/section/map/index.d.ts +4 -0
  71. package/dist/basic/registries/section/milestoneCardStrip/index.d.ts +3 -0
  72. package/dist/basic/registries/section/moreCardCarousel/index.d.ts +3 -0
  73. package/dist/basic/registries/section/ourAdvantages/index.d.ts +4 -0
  74. package/dist/basic/registries/section/overviewSplit/index.d.ts +3 -0
  75. package/dist/basic/registries/section/processTimeline/index.d.ts +3 -0
  76. package/dist/basic/registries/section/productCardStrip/index.d.ts +3 -0
  77. package/dist/basic/registries/section/resourceSection/index.d.ts +6 -0
  78. package/dist/basic/registries/section/responsiveHeroCarousel/index.d.ts +4 -0
  79. package/dist/basic/registries/section/serviceIconGrid/index.d.ts +4 -0
  80. package/dist/basic/registries/section/servicesCarousel/index.d.ts +4 -0
  81. package/dist/basic/registries/section/servicesShowcase/index.d.ts +4 -0
  82. package/dist/basic/registries/section/servicesThb/index.d.ts +4 -0
  83. package/dist/basic/registries/section/solutionList/index.d.ts +4 -0
  84. package/dist/basic/registries/section/staticPinMap/index.d.ts +4 -0
  85. package/dist/basic/registries/section/statsCards/index.d.ts +3 -0
  86. package/dist/basic/registries/section/swiperRuntime.d.ts +3 -0
  87. package/dist/basic/registries/shared/sharedTraits.d.ts +37 -0
  88. package/dist/basic/registries/types.d.ts +5 -0
  89. package/dist/basic/registries/typography/heading/index.d.ts +8 -0
  90. package/dist/basic/registries/typography/index.d.ts +2 -0
  91. package/dist/basic/registries/typography/textEditor/index.d.ts +6 -0
  92. package/dist/basic/registryManifest.d.ts +9 -0
  93. package/dist/basic/styleHelpers.d.ts +29 -0
  94. package/dist/basic/svgIcon.d.ts +11 -0
  95. package/dist/basic/traitBridge.d.ts +20 -0
  96. package/dist/basic/traitFactory.d.ts +106 -0
  97. package/dist/basic.js +1146 -0
  98. package/dist/cms/cmsFactory.d.ts +164 -0
  99. package/dist/cms/dynamicRenderPipeline.d.ts +12 -0
  100. package/dist/cms/index.d.ts +31 -0
  101. package/dist/cms/plugin.d.ts +12 -0
  102. package/dist/cms/publisher.d.ts +7 -0
  103. package/dist/cms/registries/dynamic/cms/constants.d.ts +16 -0
  104. package/dist/cms/registries/dynamic/cms/dynamicField/bindings.d.ts +78 -0
  105. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/breadcrumbBlock.d.ts +1 -0
  106. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/conditionalBlock.d.ts +1 -0
  107. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/datetimeBlock.d.ts +1 -0
  108. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/htmlBlock.d.ts +1 -0
  109. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/imageBlock.d.ts +1 -0
  110. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/linkBlock.d.ts +1 -0
  111. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/seoBlock.d.ts +1 -0
  112. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/textBlock.d.ts +1 -0
  113. package/dist/cms/registries/dynamic/cms/dynamicField/blocks/tocBlock.d.ts +1 -0
  114. package/dist/cms/registries/dynamic/cms/dynamicField/constants.d.ts +19 -0
  115. package/dist/cms/registries/dynamic/cms/dynamicField/helpers.d.ts +52 -0
  116. package/dist/cms/registries/dynamic/cms/dynamicField/registerBlock.d.ts +57 -0
  117. package/dist/cms/registries/dynamic/cms/dynamicField/styles.d.ts +7 -0
  118. package/dist/cms/registries/dynamic/cms/media/previewMediaTrait.d.ts +7 -0
  119. package/dist/cms/registries/dynamic/cms/menu/siteMenuAttrs.d.ts +11 -0
  120. package/dist/cms/registries/dynamic/cms/menuTree/menuTreeAttrs.d.ts +8 -0
  121. package/dist/cms/registries/dynamic/cms/post/styles.d.ts +6 -0
  122. package/dist/cms/registries/dynamic/cms/product/detail.styles.d.ts +1 -0
  123. package/dist/cms/registries/dynamic/cms/product/detailV2.styles.d.ts +1 -0
  124. package/dist/cms/registries/dynamic/cms/product/previewProductTrait.d.ts +6 -0
  125. package/dist/cms/registries/dynamic/cms/product/styles.d.ts +6 -0
  126. package/dist/cms/registries/dynamic/dataProvider.d.ts +45 -0
  127. package/dist/cms/registries/dynamic/loopGrid/paginationStyles.d.ts +5 -0
  128. package/dist/cms/registries/dynamic/loopGrid/preview.d.ts +16 -0
  129. package/dist/cms/registries/dynamic/loopGrid/publisher.d.ts +3 -0
  130. package/dist/cms/registries/dynamic/loopGrid/types.d.ts +97 -0
  131. package/dist/cms/registries/navigation/navbarThb/index.d.ts +3 -0
  132. package/dist/cms/registries/navigation/navbarThb/script.d.ts +1 -0
  133. package/dist/cms/registries/navigation/navbarThb/style.d.ts +1 -0
  134. package/dist/cms.js +4535 -0
  135. package/dist/global-settings/components/FontFamilySelect.vue.d.ts +29 -0
  136. package/dist/global-settings/components/FontManagerPanel.vue.d.ts +37 -0
  137. package/dist/global-settings/index.d.ts +8 -0
  138. package/dist/global-settings/plugin.d.ts +3 -0
  139. package/dist/global-settings/publisher.d.ts +15 -0
  140. package/dist/global-settings/runtime/canvasInjection.d.ts +13 -0
  141. package/dist/global-settings/runtime/panelDraftSave.d.ts +17 -0
  142. package/dist/global-settings/runtime/settingsSource.d.ts +4 -0
  143. package/dist/global-settings/useFontManager.d.ts +38 -0
  144. package/dist/global-settings/useGoogleFonts.d.ts +20 -0
  145. package/dist/global-settings/vue.d.ts +1 -0
  146. package/dist/global-settings.js +66 -0
  147. package/dist/i18n/I18nPanel.vue.d.ts +23 -0
  148. package/dist/i18n/i18n.d.ts +25 -0
  149. package/dist/i18n/index.d.ts +7 -0
  150. package/dist/i18n/languageOrder.d.ts +9 -0
  151. package/dist/i18n/plugin.d.ts +21 -0
  152. package/dist/i18n/types.d.ts +101 -0
  153. package/dist/i18n/useWebBuilderI18n.d.ts +164 -0
  154. package/dist/i18n/vue.d.ts +1 -0
  155. package/dist/i18n-BYR3l48y.js +959 -0
  156. package/dist/i18n.js +929 -0
  157. package/dist/index-CxJlLwvG.js +35378 -0
  158. package/dist/index-DWfJ4PBm.js +5724 -0
  159. package/dist/index.d.ts +9 -0
  160. package/dist/index.js +12 -0
  161. package/dist/layout-template/components/LayoutPanel.vue.d.ts +37 -0
  162. package/dist/layout-template/components/TemplateRulesPanel.vue.d.ts +41 -0
  163. package/dist/layout-template/config/layoutSharedResources.d.ts +9 -0
  164. package/dist/layout-template/config/templateSharedResources.d.ts +28 -0
  165. package/dist/layout-template/index.d.ts +9 -0
  166. package/dist/layout-template/plugin.d.ts +13 -0
  167. package/dist/layout-template/runtime/storageAdapter.d.ts +49 -0
  168. package/dist/layout-template/utils/layoutProjectData.d.ts +15 -0
  169. package/dist/layout-template/utils/layoutRulePages.d.ts +19 -0
  170. package/dist/layout-template/utils/layoutSettings.d.ts +45 -0
  171. package/dist/layout-template/utils/templateRules.d.ts +52 -0
  172. package/dist/layout-template/vue.d.ts +2 -0
  173. package/dist/layout-template.js +435 -0
  174. package/dist/layoutSettings-D4SYUMri.js +252 -0
  175. package/dist/plugin-BPA8qlaC.js +40 -0
  176. package/dist/plugin-C0PrxrIe.js +228 -0
  177. package/dist/plugin-DQshk1sY.js +361 -0
  178. package/dist/plugin-DebyCjXx.js +191 -0
  179. package/dist/plugin-Dr6TOtyH.js +73 -0
  180. package/dist/publisher/index.d.ts +5 -0
  181. package/dist/publisher/publisherAssets.d.ts +9 -0
  182. package/dist/publisher/publisherComponents.d.ts +7 -0
  183. package/dist/publisher/publisherPlugins.d.ts +12 -0
  184. package/dist/publisher-C6VWXq8u.js +25 -0
  185. package/dist/publisher.js +1711 -0
  186. package/dist/solar-BsElUqfQ.js +29843 -0
  187. package/dist/style.css +1181 -0
  188. package/dist/templateSharedResources-D1u7eFIs.js +89 -0
  189. package/dist/types-DNbok59z.js +2359 -0
  190. package/dist/useFontManager-CdrLq1eG.js +336 -0
  191. package/dist/vue.d.ts +3 -0
  192. package/dist/vue.js +2171 -0
  193. package/package.json +77 -0
package/dist/vue.js ADDED
@@ -0,0 +1,2171 @@
1
+ import './style.css';
2
+ import { defineComponent, ref, shallowRef, computed, watch, nextTick, onBeforeUnmount, openBlock, createBlock, unref, withCtx, createElementVNode, withModifiers, createVNode, normalizeClass, createElementBlock, Fragment, renderList, normalizeStyle, toDisplayString, createCommentVNode, createTextVNode, onMounted } from "vue";
3
+ import { ElPopover, ElInput, ElSelect, ElOption, ElSwitch, ElPopconfirm, ElInputNumber, ElDatePicker } from "element-plus";
4
+ import { Icon } from "@iconify/vue";
5
+ import { u as useFontManager, a as useGoogleFonts, i as injectGoogleFontCss } from "./useFontManager-CdrLq1eG.js";
6
+ import { d as collectI18nKeysFromModel, j as isModelI18nSkipped, g as getWebBuilderI18nEntryId, s as setModelI18nSkipped } from "./i18n-BYR3l48y.js";
7
+ import { a as getPageLayoutSlot, g as getGrapesPageId, h as getGrapesPageName, o as layoutTargetMatchesPage, d as cloneLayoutSettings } from "./layoutSettings-D4SYUMri.js";
8
+ const _hoisted_1$4 = { class: "wb-font-trigger-label" };
9
+ const _hoisted_2$4 = { class: "wb-fp-search" };
10
+ const _hoisted_3$4 = { class: "wb-fp-tabs" };
11
+ const _hoisted_4$4 = ["onClick"];
12
+ const _hoisted_5$4 = {
13
+ key: 0,
14
+ class: "wb-fp-empty"
15
+ };
16
+ const _hoisted_6$4 = ["onClick"];
17
+ const _hoisted_7$4 = {
18
+ key: 0,
19
+ class: "wb-fp-empty"
20
+ };
21
+ const _hoisted_8$4 = {
22
+ key: 0,
23
+ class: "wb-fp-loading"
24
+ };
25
+ const _hoisted_9$4 = ["data-google-name", "onClick"];
26
+ const _hoisted_10$4 = {
27
+ key: 0,
28
+ class: "wb-fp-sentinel-hint"
29
+ };
30
+ const _hoisted_11$4 = {
31
+ key: 0,
32
+ class: "wb-fp-empty"
33
+ };
34
+ const _hoisted_12$4 = {
35
+ key: 1,
36
+ class: "wb-fp-end"
37
+ };
38
+ const PAGE_SIZE$1 = 40;
39
+ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
40
+ __name: "FontFamilySelect",
41
+ props: {
42
+ modelValue: {},
43
+ grapes: {}
44
+ },
45
+ emits: ["update:modelValue", "select"],
46
+ setup(__props, { emit: __emit }) {
47
+ var _a;
48
+ const SYSTEM_FONTS = [
49
+ { value: "", label: "默认" },
50
+ { value: "Arial, sans-serif", label: "Arial" },
51
+ { value: '"Helvetica Neue", Helvetica, Arial, sans-serif', label: "Helvetica Neue" },
52
+ { value: "Verdana, Geneva, sans-serif", label: "Verdana" },
53
+ { value: '"Trebuchet MS", Helvetica, sans-serif', label: "Trebuchet MS" },
54
+ { value: "Tahoma, Geneva, sans-serif", label: "Tahoma" },
55
+ { value: 'Georgia, "Times New Roman", serif', label: "Georgia" },
56
+ { value: '"Times New Roman", Times, serif', label: "Times New Roman" },
57
+ { value: 'Palatino, "Palatino Linotype", "Book Antiqua", serif', label: "Palatino" },
58
+ { value: '"Courier New", Courier, monospace', label: "Courier New" },
59
+ { value: '"Lucida Console", Monaco, monospace', label: "Lucida Console" },
60
+ { value: '"PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif', label: "PingFang SC(苹方)" },
61
+ { value: '"Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif', label: "微软雅黑" },
62
+ { value: '"Source Han Sans CN", "Noto Sans CJK SC", "PingFang SC", sans-serif', label: "思源黑体" },
63
+ { value: '"Source Han Serif CN", "Noto Serif CJK SC", serif', label: "思源宋体" },
64
+ { value: '"STSong", "SimSun", serif', label: "宋体" },
65
+ { value: '"STKaiti", "KaiTi", serif', label: "楷体" }
66
+ ];
67
+ const props = __props;
68
+ const emit = __emit;
69
+ const visible = ref(false);
70
+ const activeTab = ref("installed");
71
+ const search = ref("");
72
+ const editorRef = shallowRef(null);
73
+ const fontManager = useFontManager();
74
+ const { allFonts, loading, load, search: searchFonts } = useGoogleFonts();
75
+ (_a = props.grapes) == null ? void 0 : _a.onInit((editor) => {
76
+ editorRef.value = editor;
77
+ });
78
+ const canvasDoc = computed(() => {
79
+ var _a2, _b, _c;
80
+ try {
81
+ return ((_c = (_b = (_a2 = editorRef.value) == null ? void 0 : _a2.Canvas) == null ? void 0 : _b.getDocument) == null ? void 0 : _c.call(_b)) ?? null;
82
+ } catch {
83
+ return null;
84
+ }
85
+ });
86
+ const currentLabel = computed(() => {
87
+ const val = props.modelValue;
88
+ if (!val)
89
+ return "默认字体";
90
+ const installed = fontManager.installedFonts.value.find((font) => font.cssFamily === val);
91
+ if (installed)
92
+ return installed.family;
93
+ const systemFont = SYSTEM_FONTS.find((font) => font.value === val);
94
+ if (systemFont)
95
+ return systemFont.label;
96
+ const googleFont = allFonts.value.find((font) => font.cssFamily === val);
97
+ if (googleFont)
98
+ return googleFont.family;
99
+ return val.replace(/^["']?([^"',]+)["']?.*$/, "$1").trim();
100
+ });
101
+ const filteredSystemFonts = computed(() => {
102
+ const q = search.value.toLowerCase();
103
+ return q ? SYSTEM_FONTS.filter((font) => font.label.toLowerCase().includes(q)) : SYSTEM_FONTS;
104
+ });
105
+ const filteredGoogleFonts = computed(() => searchFonts(search.value));
106
+ const displayCount = ref(PAGE_SIZE$1);
107
+ const listRef = ref(null);
108
+ const sentinelRef = ref(null);
109
+ watch(search, () => {
110
+ displayCount.value = PAGE_SIZE$1;
111
+ });
112
+ watch(activeTab, () => {
113
+ displayCount.value = PAGE_SIZE$1;
114
+ });
115
+ const visibleGoogleFonts = computed(
116
+ () => filteredGoogleFonts.value.slice(0, displayCount.value)
117
+ );
118
+ const hasMore = computed(
119
+ () => displayCount.value < filteredGoogleFonts.value.length
120
+ );
121
+ let sentinelObserver = null;
122
+ function setupSentinelObserver() {
123
+ sentinelObserver == null ? void 0 : sentinelObserver.disconnect();
124
+ if (!sentinelRef.value)
125
+ return;
126
+ sentinelObserver = new IntersectionObserver(
127
+ (entries) => {
128
+ var _a2;
129
+ if (((_a2 = entries[0]) == null ? void 0 : _a2.isIntersecting) && hasMore.value) {
130
+ displayCount.value += PAGE_SIZE$1;
131
+ }
132
+ },
133
+ { root: listRef.value, threshold: 0.1 }
134
+ );
135
+ sentinelObserver.observe(sentinelRef.value);
136
+ }
137
+ watch([activeTab, visibleGoogleFonts], async () => {
138
+ if (activeTab.value !== "google")
139
+ return;
140
+ await nextTick();
141
+ setupSentinelObserver();
142
+ }, { flush: "post" });
143
+ onBeforeUnmount(() => sentinelObserver == null ? void 0 : sentinelObserver.disconnect());
144
+ const fontPreviewObserver = ref(null);
145
+ function setupFontPreviewObserver() {
146
+ var _a2;
147
+ (_a2 = fontPreviewObserver.value) == null ? void 0 : _a2.disconnect();
148
+ fontPreviewObserver.value = new IntersectionObserver(
149
+ (entries) => {
150
+ entries.forEach((entry) => {
151
+ var _a3;
152
+ if (!entry.isIntersecting)
153
+ return;
154
+ const googleName = entry.target.dataset.googleName;
155
+ if (googleName) {
156
+ injectGoogleFontCss(googleName, canvasDoc.value);
157
+ (_a3 = fontPreviewObserver.value) == null ? void 0 : _a3.unobserve(entry.target);
158
+ }
159
+ });
160
+ },
161
+ { root: listRef.value, rootMargin: "100px", threshold: 0 }
162
+ );
163
+ }
164
+ async function observeFontItems() {
165
+ var _a2;
166
+ await nextTick();
167
+ if (!fontPreviewObserver.value)
168
+ return;
169
+ const items = ((_a2 = listRef.value) == null ? void 0 : _a2.querySelectorAll("[data-google-name]")) ?? [];
170
+ items.forEach((el) => {
171
+ var _a3;
172
+ return (_a3 = fontPreviewObserver.value) == null ? void 0 : _a3.observe(el);
173
+ });
174
+ }
175
+ watch(visibleGoogleFonts, async () => {
176
+ if (activeTab.value !== "google")
177
+ return;
178
+ await nextTick();
179
+ observeFontItems();
180
+ }, { flush: "post" });
181
+ onBeforeUnmount(() => {
182
+ var _a2;
183
+ return (_a2 = fontPreviewObserver.value) == null ? void 0 : _a2.disconnect();
184
+ });
185
+ async function onShow() {
186
+ displayCount.value = PAGE_SIZE$1;
187
+ if (allFonts.value.length === 0)
188
+ await load();
189
+ if (activeTab.value === "google") {
190
+ await nextTick();
191
+ setupFontPreviewObserver();
192
+ observeFontItems();
193
+ setupSentinelObserver();
194
+ }
195
+ }
196
+ watch(activeTab, async (tab) => {
197
+ if (tab !== "google")
198
+ return;
199
+ await nextTick();
200
+ setupFontPreviewObserver();
201
+ observeFontItems();
202
+ setupSentinelObserver();
203
+ });
204
+ function selectSystemFont(font) {
205
+ emit("update:modelValue", font.value);
206
+ emit("select", {
207
+ value: font.value,
208
+ source: "system"
209
+ });
210
+ visible.value = false;
211
+ search.value = "";
212
+ }
213
+ function selectInstalledFont(font) {
214
+ emit("update:modelValue", font.cssFamily);
215
+ emit("select", {
216
+ value: font.cssFamily,
217
+ source: "installed",
218
+ family: font.family,
219
+ googleName: font.googleName
220
+ });
221
+ visible.value = false;
222
+ search.value = "";
223
+ }
224
+ function selectGoogleFont(family, googleName, cssFamily) {
225
+ injectGoogleFontCss(googleName, canvasDoc.value);
226
+ emit("update:modelValue", cssFamily);
227
+ emit("select", {
228
+ value: cssFamily,
229
+ source: "google",
230
+ family,
231
+ googleName
232
+ });
233
+ visible.value = false;
234
+ search.value = "";
235
+ }
236
+ return (_ctx, _cache) => {
237
+ return openBlock(), createBlock(unref(ElPopover), {
238
+ visible: visible.value,
239
+ "onUpdate:visible": _cache[7] || (_cache[7] = ($event) => visible.value = $event),
240
+ placement: "bottom-start",
241
+ width: 270,
242
+ trigger: "click",
243
+ "popper-class": "wb-font-popover",
244
+ onShow
245
+ }, {
246
+ reference: withCtx(() => [
247
+ createElementVNode("button", {
248
+ class: "wb-font-trigger",
249
+ style: normalizeStyle(_ctx.modelValue ? { fontFamily: _ctx.modelValue } : {}),
250
+ type: "button"
251
+ }, [
252
+ createElementVNode("span", _hoisted_1$4, toDisplayString(currentLabel.value), 1),
253
+ createVNode(unref(Icon), {
254
+ icon: "lucide:chevron-down",
255
+ size: 12,
256
+ class: "text-gray-400 flex-shrink-0"
257
+ })
258
+ ], 4)
259
+ ]),
260
+ default: withCtx(() => [
261
+ createElementVNode("div", {
262
+ class: "wb-font-panel",
263
+ onMousedown: _cache[5] || (_cache[5] = withModifiers(() => {
264
+ }, ["stop"])),
265
+ onPointerdown: _cache[6] || (_cache[6] = withModifiers(() => {
266
+ }, ["stop"]))
267
+ }, [
268
+ createElementVNode("div", _hoisted_2$4, [
269
+ createVNode(unref(ElInput), {
270
+ modelValue: search.value,
271
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
272
+ size: "small",
273
+ placeholder: "搜索字体...",
274
+ clearable: ""
275
+ }, {
276
+ prefix: withCtx(() => [
277
+ createVNode(unref(Icon), {
278
+ icon: "lucide:search",
279
+ size: 12
280
+ })
281
+ ]),
282
+ _: 1
283
+ }, 8, ["modelValue"])
284
+ ]),
285
+ createElementVNode("div", _hoisted_3$4, [
286
+ createElementVNode("button", {
287
+ class: normalizeClass(["wb-fp-tab", { "wb-fp-tab--active": activeTab.value === "installed" }]),
288
+ type: "button",
289
+ onClick: _cache[1] || (_cache[1] = ($event) => activeTab.value = "installed")
290
+ }, " 已安装 ", 2),
291
+ createElementVNode("button", {
292
+ class: normalizeClass(["wb-fp-tab", { "wb-fp-tab--active": activeTab.value === "system" }]),
293
+ type: "button",
294
+ onClick: _cache[2] || (_cache[2] = ($event) => activeTab.value = "system")
295
+ }, " 系统字体 ", 2),
296
+ createElementVNode("button", {
297
+ class: normalizeClass(["wb-fp-tab", { "wb-fp-tab--active": activeTab.value === "google" }]),
298
+ type: "button",
299
+ onClick: _cache[3] || (_cache[3] = ($event) => activeTab.value = "google")
300
+ }, " Google 字体 ", 2)
301
+ ]),
302
+ createElementVNode("div", {
303
+ ref_key: "listRef",
304
+ ref: listRef,
305
+ class: "wb-fp-list"
306
+ }, [
307
+ activeTab.value === "installed" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
308
+ createElementVNode("button", {
309
+ class: normalizeClass(["wb-fp-item", { "wb-fp-item--active": !_ctx.modelValue }]),
310
+ type: "button",
311
+ onClick: _cache[4] || (_cache[4] = ($event) => selectSystemFont({ value: "" }))
312
+ }, _cache[8] || (_cache[8] = [
313
+ createElementVNode("span", { class: "wb-fp-name" }, "默认字体", -1)
314
+ ]), 2),
315
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(fontManager).installedFonts.value, (font) => {
316
+ return openBlock(), createElementBlock("button", {
317
+ key: font.family,
318
+ class: normalizeClass(["wb-fp-item", { "wb-fp-item--active": _ctx.modelValue === font.cssFamily }]),
319
+ type: "button",
320
+ onClick: ($event) => selectInstalledFont(font)
321
+ }, [
322
+ createElementVNode("span", {
323
+ class: "wb-fp-name",
324
+ style: normalizeStyle({ fontFamily: font.cssFamily })
325
+ }, toDisplayString(font.family), 5),
326
+ createElementVNode("span", {
327
+ class: "wb-fp-preview",
328
+ style: normalizeStyle({ fontFamily: font.cssFamily })
329
+ }, " Aa ", 4)
330
+ ], 10, _hoisted_4$4);
331
+ }), 128)),
332
+ unref(fontManager).installedFonts.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_5$4, " 暂无已安装字体,请在「全局设置 → 字体管理」中安装 ")) : createCommentVNode("", true)
333
+ ], 64)) : activeTab.value === "system" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
334
+ (openBlock(true), createElementBlock(Fragment, null, renderList(filteredSystemFonts.value, (font) => {
335
+ return openBlock(), createElementBlock("button", {
336
+ key: font.value,
337
+ class: normalizeClass(["wb-fp-item", { "wb-fp-item--active": _ctx.modelValue === font.value }]),
338
+ type: "button",
339
+ onClick: ($event) => selectSystemFont(font)
340
+ }, [
341
+ createElementVNode("span", {
342
+ class: "wb-fp-name",
343
+ style: normalizeStyle(font.value ? { fontFamily: font.value } : {})
344
+ }, toDisplayString(font.label), 5),
345
+ font.value ? (openBlock(), createElementBlock("span", {
346
+ key: 0,
347
+ class: "wb-fp-preview",
348
+ style: normalizeStyle({ fontFamily: font.value })
349
+ }, " Aa ", 4)) : createCommentVNode("", true)
350
+ ], 10, _hoisted_6$4);
351
+ }), 128)),
352
+ filteredSystemFonts.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_7$4, "无匹配字体")) : createCommentVNode("", true)
353
+ ], 64)) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [
354
+ unref(loading) ? (openBlock(), createElementBlock("div", _hoisted_8$4, [
355
+ createVNode(unref(Icon), {
356
+ icon: "lucide:loader-2",
357
+ class: "animate-spin",
358
+ size: 16
359
+ }),
360
+ _cache[9] || (_cache[9] = createElementVNode("span", null, "加载字体列表...", -1))
361
+ ])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
362
+ (openBlock(true), createElementBlock(Fragment, null, renderList(visibleGoogleFonts.value, (font) => {
363
+ return openBlock(), createElementBlock("button", {
364
+ key: font.family,
365
+ class: normalizeClass(["wb-fp-item", { "wb-fp-item--active": _ctx.modelValue === font.cssFamily }]),
366
+ type: "button",
367
+ "data-google-name": font.googleName,
368
+ onClick: ($event) => selectGoogleFont(font.family, font.googleName, font.cssFamily)
369
+ }, [
370
+ createElementVNode("span", {
371
+ class: "wb-fp-name",
372
+ style: normalizeStyle({ fontFamily: font.cssFamily })
373
+ }, toDisplayString(font.family), 5),
374
+ createElementVNode("span", {
375
+ class: "wb-fp-preview",
376
+ style: normalizeStyle({ fontFamily: font.cssFamily })
377
+ }, " Aa ", 4)
378
+ ], 10, _hoisted_9$4);
379
+ }), 128)),
380
+ createElementVNode("div", {
381
+ ref_key: "sentinelRef",
382
+ ref: sentinelRef,
383
+ class: "wb-fp-sentinel"
384
+ }, [
385
+ hasMore.value ? (openBlock(), createElementBlock("span", _hoisted_10$4, [
386
+ createVNode(unref(Icon), {
387
+ icon: "lucide:loader-2",
388
+ class: "animate-spin",
389
+ size: 13
390
+ })
391
+ ])) : createCommentVNode("", true)
392
+ ], 512),
393
+ !hasMore.value && filteredGoogleFonts.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_11$4, " 无匹配字体 ")) : createCommentVNode("", true),
394
+ !hasMore.value && filteredGoogleFonts.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_12$4, " 共 " + toDisplayString(filteredGoogleFonts.value.length) + " 个字体 ", 1)) : createCommentVNode("", true)
395
+ ], 64))
396
+ ], 64))
397
+ ], 512)
398
+ ], 32)
399
+ ]),
400
+ _: 1
401
+ }, 8, ["visible"]);
402
+ };
403
+ }
404
+ });
405
+ const _export_sfc = (sfc, props) => {
406
+ const target = sfc.__vccOpts || sfc;
407
+ for (const [key, val] of props) {
408
+ target[key] = val;
409
+ }
410
+ return target;
411
+ };
412
+ const FontFamilySelect = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-e45386ba"]]);
413
+ const _hoisted_1$3 = { class: "font-manager" };
414
+ const _hoisted_2$3 = { class: "fm-header" };
415
+ const _hoisted_3$3 = { class: "flex items-center gap-1.5" };
416
+ const _hoisted_4$3 = { class: "text-[10px] text-gray-400" };
417
+ const _hoisted_5$3 = { class: "fm-global" };
418
+ const _hoisted_6$3 = { class: "fm-section-title" };
419
+ const _hoisted_7$3 = { class: "fm-delivery" };
420
+ const _hoisted_8$3 = ["aria-checked"];
421
+ const _hoisted_9$3 = { class: "fm-tabs" };
422
+ const _hoisted_10$3 = {
423
+ key: 0,
424
+ class: "fm-search"
425
+ };
426
+ const _hoisted_11$3 = {
427
+ key: 1,
428
+ class: "fm-list"
429
+ };
430
+ const _hoisted_12$3 = {
431
+ key: 0,
432
+ class: "fm-empty"
433
+ };
434
+ const _hoisted_13$3 = ["onClick"];
435
+ const _hoisted_14$3 = { class: "fm-font-meta" };
436
+ const _hoisted_15$3 = ["onClick"];
437
+ const _hoisted_16$3 = {
438
+ class: "fm-icon-btn text-gray-400",
439
+ type: "button"
440
+ };
441
+ const _hoisted_17$3 = {
442
+ key: 0,
443
+ class: "fm-variants"
444
+ };
445
+ const _hoisted_18$3 = { class: "fm-variant-grid" };
446
+ const _hoisted_19$3 = ["checked", "onChange"];
447
+ const _hoisted_20$3 = { class: "fm-variant-text" };
448
+ const _hoisted_21$3 = {
449
+ key: 0,
450
+ class: "fm-loading"
451
+ };
452
+ const _hoisted_22$3 = ["data-preview-font"];
453
+ const _hoisted_23$3 = { class: "fm-browse-row" };
454
+ const _hoisted_24$3 = { class: "fm-font-category" };
455
+ const _hoisted_25$1 = {
456
+ key: 0,
457
+ class: "fm-installed-badge",
458
+ type: "button",
459
+ disabled: ""
460
+ };
461
+ const _hoisted_26$1 = ["onClick"];
462
+ const _hoisted_27$1 = {
463
+ key: 0,
464
+ class: "text-gray-400"
465
+ };
466
+ const _hoisted_28$1 = {
467
+ key: 0,
468
+ class: "fm-empty"
469
+ };
470
+ const _hoisted_29$1 = {
471
+ key: 1,
472
+ class: "fm-end"
473
+ };
474
+ const PAGE_SIZE = 30;
475
+ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
476
+ __name: "FontManagerPanel",
477
+ props: {
478
+ grapes: {},
479
+ hostUi: {},
480
+ globalFontFamily: {},
481
+ fontDelivery: {}
482
+ },
483
+ emits: ["select-global-font", "reset-global-font", "update:font-delivery"],
484
+ setup(__props, { emit: __emit }) {
485
+ const props = __props;
486
+ const emit = __emit;
487
+ const fontManager = useFontManager();
488
+ const { installedFonts, googleFonts } = fontManager;
489
+ const activeView = ref("installed");
490
+ const search = ref("");
491
+ const expandedFamily = ref(null);
492
+ const isLocalFontDelivery = computed({
493
+ get: () => props.fontDelivery === "local",
494
+ set: (value) => {
495
+ emit("update:font-delivery", value ? "local" : "remote");
496
+ }
497
+ });
498
+ const { allFonts, loading, load } = googleFonts;
499
+ async function switchToBrowse() {
500
+ activeView.value = "browse";
501
+ if (allFonts.value.length === 0)
502
+ await load();
503
+ }
504
+ const filteredBrowseFonts = computed(() => {
505
+ const q = search.value.toLowerCase();
506
+ let fonts = allFonts.value;
507
+ if (q) {
508
+ fonts = fonts.filter(
509
+ (font) => font.family.toLowerCase().includes(q) || font.category.toLowerCase().includes(q)
510
+ );
511
+ }
512
+ return fonts;
513
+ });
514
+ const displayCount = ref(PAGE_SIZE);
515
+ const listRef = ref(null);
516
+ const sentinelRef = ref(null);
517
+ watch(search, () => {
518
+ displayCount.value = PAGE_SIZE;
519
+ });
520
+ watch(activeView, () => {
521
+ displayCount.value = PAGE_SIZE;
522
+ });
523
+ const visibleBrowseFonts = computed(
524
+ () => filteredBrowseFonts.value.slice(0, displayCount.value)
525
+ );
526
+ const hasMore = computed(
527
+ () => displayCount.value < filteredBrowseFonts.value.length
528
+ );
529
+ let sentinelObserver = null;
530
+ function setupSentinelObserver() {
531
+ sentinelObserver == null ? void 0 : sentinelObserver.disconnect();
532
+ if (!sentinelRef.value || !listRef.value)
533
+ return;
534
+ sentinelObserver = new IntersectionObserver(
535
+ (entries) => {
536
+ var _a;
537
+ if (((_a = entries[0]) == null ? void 0 : _a.isIntersecting) && hasMore.value) {
538
+ displayCount.value += PAGE_SIZE;
539
+ }
540
+ },
541
+ { root: listRef.value, threshold: 0.1 }
542
+ );
543
+ sentinelObserver.observe(sentinelRef.value);
544
+ }
545
+ watch([activeView, visibleBrowseFonts], async () => {
546
+ if (activeView.value !== "browse")
547
+ return;
548
+ await nextTick();
549
+ setupSentinelObserver();
550
+ }, { flush: "post" });
551
+ onBeforeUnmount(() => sentinelObserver == null ? void 0 : sentinelObserver.disconnect());
552
+ const previewObserver = ref(null);
553
+ const loadedPreviews = ref(/* @__PURE__ */ new Set());
554
+ function setupPreviewObserver() {
555
+ var _a;
556
+ (_a = previewObserver.value) == null ? void 0 : _a.disconnect();
557
+ previewObserver.value = new IntersectionObserver(
558
+ (entries) => {
559
+ entries.forEach((entry) => {
560
+ var _a2;
561
+ if (!entry.isIntersecting)
562
+ return;
563
+ const googleName = entry.target.dataset.previewFont;
564
+ if (googleName && !loadedPreviews.value.has(googleName)) {
565
+ loadedPreviews.value.add(googleName);
566
+ const href = `https://fonts.googleapis.com/css2?family=${googleName}:wght@400;700&display=swap`;
567
+ if (!document.head.querySelector(`link[data-wb-font-preview="${googleName}"]`)) {
568
+ const link = document.createElement("link");
569
+ link.rel = "stylesheet";
570
+ link.setAttribute("data-wb-font-preview", googleName);
571
+ link.href = href;
572
+ document.head.appendChild(link);
573
+ }
574
+ (_a2 = previewObserver.value) == null ? void 0 : _a2.unobserve(entry.target);
575
+ }
576
+ });
577
+ },
578
+ { root: listRef.value, rootMargin: "200px", threshold: 0 }
579
+ );
580
+ }
581
+ async function observePreviewItems() {
582
+ var _a;
583
+ await nextTick();
584
+ if (!previewObserver.value)
585
+ return;
586
+ const items = ((_a = listRef.value) == null ? void 0 : _a.querySelectorAll("[data-preview-font]")) ?? [];
587
+ items.forEach((el) => {
588
+ var _a2;
589
+ return (_a2 = previewObserver.value) == null ? void 0 : _a2.observe(el);
590
+ });
591
+ }
592
+ watch(visibleBrowseFonts, async () => {
593
+ if (activeView.value !== "browse")
594
+ return;
595
+ await nextTick();
596
+ observePreviewItems();
597
+ }, { flush: "post" });
598
+ watch(activeView, async (view) => {
599
+ if (view !== "browse")
600
+ return;
601
+ await nextTick();
602
+ setupPreviewObserver();
603
+ observePreviewItems();
604
+ setupSentinelObserver();
605
+ });
606
+ onBeforeUnmount(() => {
607
+ var _a;
608
+ return (_a = previewObserver.value) == null ? void 0 : _a.disconnect();
609
+ });
610
+ function handleInstall(font) {
611
+ fontManager.installFont(font);
612
+ }
613
+ function handleGlobalFontSelect(payload) {
614
+ emit("select-global-font", payload);
615
+ }
616
+ function resetGlobalFont() {
617
+ emit("reset-global-font");
618
+ }
619
+ async function handleRemove(font) {
620
+ var _a;
621
+ const confirmed = await ((_a = props.hostUi) == null ? void 0 : _a.confirm({
622
+ message: `确定要卸载字体 "${font.family}" 吗?已使用该字体的组件将回退到默认字体。`,
623
+ title: "卸载字体",
624
+ confirmText: "卸载",
625
+ cancelText: "取消"
626
+ }));
627
+ if (!confirmed)
628
+ return;
629
+ fontManager.removeFont(font.family);
630
+ expandedFamily.value = null;
631
+ }
632
+ function toggleVariant(font, variant) {
633
+ const has = font.variants.includes(variant);
634
+ let newVariants;
635
+ if (has) {
636
+ newVariants = font.variants.filter((v) => v !== variant);
637
+ if (newVariants.length === 0)
638
+ newVariants = ["regular"];
639
+ } else {
640
+ newVariants = [...font.variants, variant];
641
+ }
642
+ fontManager.updateVariants(font.family, newVariants);
643
+ }
644
+ function toggleExpand(family) {
645
+ expandedFamily.value = expandedFamily.value === family ? null : family;
646
+ }
647
+ function variantLabel(variant) {
648
+ const map = {
649
+ "100": "Thin",
650
+ "200": "Extra Light",
651
+ "300": "Light",
652
+ "regular": "Regular",
653
+ "500": "Medium",
654
+ "600": "Semi Bold",
655
+ "700": "Bold",
656
+ "800": "Extra Bold",
657
+ "900": "Black"
658
+ };
659
+ return map[variant] ?? variant;
660
+ }
661
+ return (_ctx, _cache) => {
662
+ return openBlock(), createElementBlock("div", _hoisted_1$3, [
663
+ createElementVNode("div", _hoisted_2$3, [
664
+ createElementVNode("div", _hoisted_3$3, [
665
+ createVNode(unref(Icon), {
666
+ icon: "lucide:type",
667
+ size: 14,
668
+ class: "text-gray-500"
669
+ }),
670
+ _cache[3] || (_cache[3] = createElementVNode("span", { class: "text-sm font-semibold text-gray-700" }, "字体管理", -1))
671
+ ]),
672
+ createElementVNode("span", _hoisted_4$3, toDisplayString(unref(installedFonts).length) + " 个已安装", 1)
673
+ ]),
674
+ createElementVNode("div", _hoisted_5$3, [
675
+ createElementVNode("div", _hoisted_6$3, [
676
+ _cache[4] || (_cache[4] = createElementVNode("span", null, "全局字体", -1)),
677
+ props.globalFontFamily ? (openBlock(), createElementBlock("button", {
678
+ key: 0,
679
+ class: "fm-reset-btn",
680
+ type: "button",
681
+ onClick: resetGlobalFont
682
+ }, " 恢复默认 ")) : createCommentVNode("", true)
683
+ ]),
684
+ createVNode(FontFamilySelect, {
685
+ "model-value": props.globalFontFamily,
686
+ grapes: props.grapes,
687
+ onSelect: handleGlobalFontSelect
688
+ }, null, 8, ["model-value", "grapes"]),
689
+ _cache[5] || (_cache[5] = createElementVNode("div", { class: "fm-helper" }, " 应用于页面正文、表单控件和 H1-H6 默认样式,最终通过 CSS 变量输出。 ", -1))
690
+ ]),
691
+ createElementVNode("div", _hoisted_7$3, [
692
+ _cache[7] || (_cache[7] = createElementVNode("div", { class: "fm-delivery-copy" }, [
693
+ createElementVNode("span", { class: "fm-delivery-title" }, "发布字体本地化"),
694
+ createElementVNode("span", { class: "fm-delivery-desc" }, "开启后发布时下载 Google Fonts 并引用本地字体文件")
695
+ ], -1)),
696
+ createElementVNode("button", {
697
+ class: normalizeClass(["fm-switch", { "fm-switch--active": isLocalFontDelivery.value }]),
698
+ type: "button",
699
+ role: "switch",
700
+ "aria-checked": isLocalFontDelivery.value,
701
+ title: "发布字体本地化",
702
+ onClick: _cache[0] || (_cache[0] = ($event) => isLocalFontDelivery.value = !isLocalFontDelivery.value)
703
+ }, _cache[6] || (_cache[6] = [
704
+ createElementVNode("span", { class: "fm-switch-thumb" }, null, -1)
705
+ ]), 10, _hoisted_8$3)
706
+ ]),
707
+ createElementVNode("div", _hoisted_9$3, [
708
+ createElementVNode("button", {
709
+ class: normalizeClass(["fm-tab", { "fm-tab--active": activeView.value === "installed" }]),
710
+ type: "button",
711
+ onClick: _cache[1] || (_cache[1] = ($event) => activeView.value = "installed")
712
+ }, " 已安装 ", 2),
713
+ createElementVNode("button", {
714
+ class: normalizeClass(["fm-tab", { "fm-tab--active": activeView.value === "browse" }]),
715
+ type: "button",
716
+ onClick: switchToBrowse
717
+ }, " 浏览字体 ", 2)
718
+ ]),
719
+ activeView.value === "browse" ? (openBlock(), createElementBlock("div", _hoisted_10$3, [
720
+ createVNode(unref(ElInput), {
721
+ modelValue: search.value,
722
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => search.value = $event),
723
+ size: "small",
724
+ placeholder: "搜索 Google 字体...",
725
+ clearable: ""
726
+ }, {
727
+ prefix: withCtx(() => [
728
+ createVNode(unref(Icon), {
729
+ icon: "lucide:search",
730
+ size: 12
731
+ })
732
+ ]),
733
+ _: 1
734
+ }, 8, ["modelValue"])
735
+ ])) : createCommentVNode("", true),
736
+ activeView.value === "installed" ? (openBlock(), createElementBlock("div", _hoisted_11$3, [
737
+ unref(installedFonts).length === 0 ? (openBlock(), createElementBlock("div", _hoisted_12$3, [
738
+ createVNode(unref(Icon), {
739
+ icon: "lucide:type",
740
+ size: 24,
741
+ class: "text-gray-300 mb-2"
742
+ }),
743
+ _cache[9] || (_cache[9] = createElementVNode("p", { class: "text-xs text-gray-400" }, "暂无已安装字体", -1)),
744
+ createElementVNode("button", {
745
+ class: "fm-browse-btn",
746
+ type: "button",
747
+ onClick: switchToBrowse
748
+ }, [
749
+ createVNode(unref(Icon), {
750
+ icon: "lucide:plus",
751
+ size: 12
752
+ }),
753
+ _cache[8] || (_cache[8] = createTextVNode(" 浏览并安装字体 "))
754
+ ])
755
+ ])) : createCommentVNode("", true),
756
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(installedFonts), (font) => {
757
+ return openBlock(), createElementBlock("div", {
758
+ key: font.family,
759
+ class: "fm-installed-item"
760
+ }, [
761
+ createElementVNode("div", {
762
+ class: "fm-installed-row",
763
+ onClick: ($event) => toggleExpand(font.family)
764
+ }, [
765
+ createElementVNode("span", {
766
+ class: "fm-font-name",
767
+ style: normalizeStyle({ fontFamily: font.cssFamily })
768
+ }, toDisplayString(font.family), 5),
769
+ createElementVNode("span", _hoisted_14$3, toDisplayString(font.variants.length) + " 个变体", 1),
770
+ createElementVNode("button", {
771
+ class: "fm-icon-btn text-gray-400 hover:text-red-500",
772
+ title: "卸载",
773
+ type: "button",
774
+ onClick: withModifiers(($event) => handleRemove(font), ["stop"])
775
+ }, [
776
+ createVNode(unref(Icon), {
777
+ icon: "lucide:trash-2",
778
+ size: 13
779
+ })
780
+ ], 8, _hoisted_15$3),
781
+ createElementVNode("button", _hoisted_16$3, [
782
+ createVNode(unref(Icon), {
783
+ icon: expandedFamily.value === font.family ? "lucide:chevron-up" : "lucide:chevron-down",
784
+ size: 13
785
+ }, null, 8, ["icon"])
786
+ ])
787
+ ], 8, _hoisted_13$3),
788
+ expandedFamily.value === font.family ? (openBlock(), createElementBlock("div", _hoisted_17$3, [
789
+ _cache[10] || (_cache[10] = createElementVNode("div", { class: "fm-variant-label" }, "选择字重变体:", -1)),
790
+ createElementVNode("div", _hoisted_18$3, [
791
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(fontManager).ALL_VARIANTS, (variant) => {
792
+ return openBlock(), createElementBlock("label", {
793
+ key: variant,
794
+ class: "fm-variant-checkbox"
795
+ }, [
796
+ createElementVNode("input", {
797
+ type: "checkbox",
798
+ checked: font.variants.includes(variant),
799
+ onChange: ($event) => toggleVariant(font, variant)
800
+ }, null, 40, _hoisted_19$3),
801
+ createElementVNode("span", _hoisted_20$3, toDisplayString(variantLabel(variant)), 1)
802
+ ]);
803
+ }), 128))
804
+ ]),
805
+ createElementVNode("div", {
806
+ class: "fm-preview-text",
807
+ style: normalizeStyle({ fontFamily: font.cssFamily, fontWeight: 400 })
808
+ }, " The quick brown fox jumps over the lazy dog ", 4),
809
+ createElementVNode("div", {
810
+ class: "fm-preview-text",
811
+ style: normalizeStyle({ fontFamily: font.cssFamily, fontWeight: 700 })
812
+ }, " The quick brown fox jumps over the lazy dog ", 4)
813
+ ])) : createCommentVNode("", true)
814
+ ]);
815
+ }), 128))
816
+ ])) : (openBlock(), createElementBlock("div", {
817
+ key: 2,
818
+ ref_key: "listRef",
819
+ ref: listRef,
820
+ class: "fm-list fm-browse-list"
821
+ }, [
822
+ unref(loading) ? (openBlock(), createElementBlock("div", _hoisted_21$3, [
823
+ createVNode(unref(Icon), {
824
+ icon: "lucide:loader-2",
825
+ class: "animate-spin",
826
+ size: 16
827
+ }),
828
+ _cache[11] || (_cache[11] = createElementVNode("span", null, "加载字体列表...", -1))
829
+ ])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
830
+ (openBlock(true), createElementBlock(Fragment, null, renderList(visibleBrowseFonts.value, (font) => {
831
+ return openBlock(), createElementBlock("div", {
832
+ key: font.family,
833
+ class: "fm-browse-item",
834
+ "data-preview-font": font.googleName
835
+ }, [
836
+ createElementVNode("div", _hoisted_23$3, [
837
+ createElementVNode("span", {
838
+ class: "fm-font-name",
839
+ style: normalizeStyle({ fontFamily: font.cssFamily })
840
+ }, toDisplayString(font.family), 5),
841
+ createElementVNode("span", _hoisted_24$3, toDisplayString(font.category), 1),
842
+ unref(fontManager).isInstalled(font.family) ? (openBlock(), createElementBlock("button", _hoisted_25$1, [
843
+ createVNode(unref(Icon), {
844
+ icon: "lucide:check",
845
+ size: 11
846
+ }),
847
+ _cache[12] || (_cache[12] = createTextVNode(" 已安装 "))
848
+ ])) : (openBlock(), createElementBlock("button", {
849
+ key: 1,
850
+ class: "fm-install-btn",
851
+ type: "button",
852
+ onClick: ($event) => handleInstall(font)
853
+ }, [
854
+ createVNode(unref(Icon), {
855
+ icon: "lucide:plus",
856
+ size: 11
857
+ }),
858
+ _cache[13] || (_cache[13] = createTextVNode(" 安装 "))
859
+ ], 8, _hoisted_26$1))
860
+ ])
861
+ ], 8, _hoisted_22$3);
862
+ }), 128)),
863
+ createElementVNode("div", {
864
+ ref_key: "sentinelRef",
865
+ ref: sentinelRef,
866
+ class: "fm-sentinel"
867
+ }, [
868
+ hasMore.value ? (openBlock(), createElementBlock("span", _hoisted_27$1, [
869
+ createVNode(unref(Icon), {
870
+ icon: "lucide:loader-2",
871
+ class: "animate-spin",
872
+ size: 13
873
+ })
874
+ ])) : createCommentVNode("", true)
875
+ ], 512),
876
+ !hasMore.value && filteredBrowseFonts.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_28$1, _cache[14] || (_cache[14] = [
877
+ createElementVNode("p", { class: "text-xs text-gray-400" }, "无匹配字体", -1)
878
+ ]))) : createCommentVNode("", true),
879
+ !hasMore.value && filteredBrowseFonts.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_29$1, " 共 " + toDisplayString(filteredBrowseFonts.value.length) + " 个字体 ", 1)) : createCommentVNode("", true)
880
+ ], 64))
881
+ ], 512))
882
+ ]);
883
+ };
884
+ }
885
+ });
886
+ const FontManagerPanel = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-d94b5511"]]);
887
+ const _hoisted_1$2 = { class: "wb-i18n-panel" };
888
+ const _hoisted_2$2 = { class: "wb-i18n-panel__header" };
889
+ const _hoisted_3$2 = { class: "wb-i18n-panel__meta" };
890
+ const _hoisted_4$2 = { class: "wb-i18n-controls" };
891
+ const _hoisted_5$2 = { class: "wb-i18n-field" };
892
+ const _hoisted_6$2 = ["disabled", "aria-expanded"];
893
+ const _hoisted_7$2 = { class: "wb-i18n-select-ui__value" };
894
+ const _hoisted_8$2 = {
895
+ key: 0,
896
+ class: "wb-i18n-select-ui__menu",
897
+ role: "listbox"
898
+ };
899
+ const _hoisted_9$2 = ["aria-selected", "onClick"];
900
+ const _hoisted_10$2 = { class: "wb-i18n-field" };
901
+ const _hoisted_11$2 = ["disabled", "aria-expanded"];
902
+ const _hoisted_12$2 = { class: "wb-i18n-select-ui__value" };
903
+ const _hoisted_13$2 = {
904
+ key: 0,
905
+ class: "wb-i18n-select-ui__menu",
906
+ role: "listbox"
907
+ };
908
+ const _hoisted_14$2 = ["aria-selected", "onClick"];
909
+ const _hoisted_15$2 = {
910
+ key: 0,
911
+ class: "wb-i18n-field__tip"
912
+ };
913
+ const _hoisted_16$2 = { class: "wb-i18n-summary" };
914
+ const _hoisted_17$2 = {
915
+ key: 1,
916
+ class: "wb-i18n-error"
917
+ };
918
+ const _hoisted_18$2 = { class: "wb-i18n-panel__body" };
919
+ const _hoisted_19$2 = {
920
+ key: 0,
921
+ class: "wb-i18n-empty"
922
+ };
923
+ const _hoisted_20$2 = {
924
+ key: 1,
925
+ class: "wb-i18n-empty"
926
+ };
927
+ const _hoisted_21$2 = {
928
+ key: 2,
929
+ class: "wb-i18n-empty"
930
+ };
931
+ const _hoisted_22$2 = { class: "wb-i18n-field wb-i18n-picker" };
932
+ const _hoisted_23$2 = ["aria-expanded"];
933
+ const _hoisted_24$2 = { class: "wb-i18n-select-ui__value" };
934
+ const _hoisted_25 = {
935
+ key: 0,
936
+ class: "wb-i18n-select-ui__menu wb-i18n-select-ui__menu--tall",
937
+ role: "listbox"
938
+ };
939
+ const _hoisted_26 = ["aria-selected", "onClick"];
940
+ const _hoisted_27 = {
941
+ key: 0,
942
+ class: "wb-i18n-editor"
943
+ };
944
+ const _hoisted_28 = { class: "wb-i18n-editor__bar" };
945
+ const _hoisted_29 = { class: "wb-i18n-editor__title" };
946
+ const _hoisted_30 = {
947
+ key: 0,
948
+ class: "wb-i18n-dirty"
949
+ };
950
+ const _hoisted_31 = { class: "wb-i18n-translation-stack" };
951
+ const _hoisted_32 = { class: "wb-i18n-translation-block" };
952
+ const _hoisted_33 = ["value"];
953
+ const _hoisted_34 = { class: "wb-i18n-translation-block wb-i18n-translation-block--target" };
954
+ const _hoisted_35 = ["value"];
955
+ const _hoisted_36 = { class: "wb-i18n-actions wb-i18n-actions--inline" };
956
+ const _hoisted_37 = ["disabled"];
957
+ const _hoisted_38 = { class: "wb-i18n-actions wb-i18n-panel__footer" };
958
+ const _hoisted_39 = ["disabled"];
959
+ const _hoisted_40 = ["disabled"];
960
+ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
961
+ __name: "I18nPanel",
962
+ props: {
963
+ manager: {},
964
+ selectedComponent: {},
965
+ selectedRevision: {}
966
+ },
967
+ setup(__props) {
968
+ const props = __props;
969
+ const fieldLabels = {
970
+ text: "文本",
971
+ html: "HTML",
972
+ alt: "图片 Alt",
973
+ title: "Title",
974
+ placeholder: "Placeholder",
975
+ "aria-label": "ARIA Label",
976
+ "seo.title": "SEO 标题",
977
+ "seo.description": "SEO 描述",
978
+ "seo.keywords": "SEO 关键词"
979
+ };
980
+ const statusLabels = {
981
+ translated: "已翻译",
982
+ missing: "缺失",
983
+ stale: "源文已变更",
984
+ pending: "待翻译",
985
+ source_changed: "源文已变更",
986
+ draft: "草稿",
987
+ empty: "空内容",
988
+ error: "翻译失败"
989
+ };
990
+ const statusClass = {
991
+ translated: "wb-i18n-status--translated",
992
+ missing: "wb-i18n-status--missing",
993
+ stale: "wb-i18n-status--stale",
994
+ pending: "wb-i18n-status--missing",
995
+ source_changed: "wb-i18n-status--stale",
996
+ draft: "wb-i18n-status--stale",
997
+ empty: "wb-i18n-status--missing",
998
+ error: "wb-i18n-status--error"
999
+ };
1000
+ const engineLabels = {
1001
+ qwen_mt: "Qwen-MT",
1002
+ qwen: "Qwen-MT",
1003
+ qwenmt: "Qwen-MT",
1004
+ deepseek: "DeepSeek",
1005
+ deepl: "DeepL",
1006
+ tencent_tmt: "腾讯云 TMT",
1007
+ volcengine_mt: "火山引擎翻译"
1008
+ };
1009
+ const selectedEntryId = ref("");
1010
+ const openSelectId = ref("");
1011
+ const providerOptions = ref([]);
1012
+ const providerLoading = ref(false);
1013
+ const providerConfigError = ref("");
1014
+ const noTranslateRevision = ref(0);
1015
+ const selectedComponent = computed(() => unref(props.selectedComponent));
1016
+ const selectedRevision = computed(
1017
+ () => props.selectedRevision === void 0 ? 0 : unref(props.selectedRevision)
1018
+ );
1019
+ const selectedKeyDependency = computed(
1020
+ () => {
1021
+ var _a;
1022
+ return `${props.manager.sourceRevision.value}:${selectedRevision.value}:${((_a = selectedComponent.value) == null ? void 0 : _a.cid) ?? ""}`;
1023
+ }
1024
+ );
1025
+ const selectedKeys = computed(() => {
1026
+ if (!selectedKeyDependency.value && !selectedComponent.value)
1027
+ return /* @__PURE__ */ new Set();
1028
+ return collectI18nKeysFromModel(selectedComponent.value, { includeAncestors: true });
1029
+ });
1030
+ const hasSelectedComponent = computed(() => Boolean(selectedComponent.value));
1031
+ const selectedNoTranslate = computed(() => {
1032
+ void noTranslateRevision.value;
1033
+ void selectedKeyDependency.value;
1034
+ return isModelI18nSkipped(selectedComponent.value);
1035
+ });
1036
+ const scopedEntries = computed(() => {
1037
+ if (hasSelectedComponent.value && !selectedKeys.value.size)
1038
+ return [];
1039
+ const keys = selectedKeys.value;
1040
+ if (!keys.size)
1041
+ return props.manager.entries.value;
1042
+ const selectedEntries = props.manager.entries.value.filter((entry) => keys.has(entry.key));
1043
+ return selectedEntries.length ? selectedEntries : props.manager.entries.value;
1044
+ });
1045
+ const visibleEntries = computed(() => scopedEntries.value);
1046
+ const selectedOnly = computed(() => hasSelectedComponent.value);
1047
+ const dirtyIds = computed(() => new Set(props.manager.dirtyEntryIds.value));
1048
+ const isActionableEntry = (entry) => entry.status === "missing" || entry.status === "stale" || entry.status === "error";
1049
+ const activeEntry = computed(() => {
1050
+ return visibleEntries.value.find(
1051
+ (entry) => getWebBuilderI18nEntryId(entry) === selectedEntryId.value
1052
+ ) || visibleEntries.value[0] || null;
1053
+ });
1054
+ const hasEnabledProvider = computed(() => providerOptions.value.length > 0);
1055
+ const visibleActionableEntries = computed(() => visibleEntries.value.filter(isActionableEntry));
1056
+ const hasActionableEntries = computed(() => props.manager.actionableEntries.value.length > 0);
1057
+ const localeOptions = computed(() => {
1058
+ const languages = props.manager.targetLanguages.value;
1059
+ if (!languages.length) {
1060
+ return [{ key: "empty-locale", label: "未启用目标语言", value: "" }];
1061
+ }
1062
+ return languages.map((lang) => ({
1063
+ key: lang.value,
1064
+ label: lang.label,
1065
+ value: lang.value
1066
+ }));
1067
+ });
1068
+ const localeSelectDisabled = computed(() => !props.manager.targetLanguages.value.length);
1069
+ const providerSelectDisabled = computed(() => providerLoading.value || !providerOptions.value.length);
1070
+ const providerSelectOptions = computed(() => {
1071
+ if (providerLoading.value) {
1072
+ return [{ key: "provider-loading", label: "加载启用引擎...", value: "" }];
1073
+ }
1074
+ if (!providerOptions.value.length) {
1075
+ return [{ key: "provider-empty", label: "未启用翻译引擎", value: "" }];
1076
+ }
1077
+ return providerOptions.value;
1078
+ });
1079
+ const entryOptions = computed(
1080
+ () => visibleEntries.value.map((entry) => ({
1081
+ key: getWebBuilderI18nEntryId(entry),
1082
+ label: entryOptionLabel(entry),
1083
+ value: getWebBuilderI18nEntryId(entry)
1084
+ }))
1085
+ );
1086
+ const sourceLanguageLabel = computed(() => {
1087
+ var _a;
1088
+ const current = props.manager.sourceLocale.value;
1089
+ return ((_a = props.manager.languages.value.find((lang) => lang.value === current)) == null ? void 0 : _a.label) || current || "源语言";
1090
+ });
1091
+ const targetLanguageLabel = computed(() => {
1092
+ var _a;
1093
+ const current = props.manager.locale.value;
1094
+ return ((_a = props.manager.languages.value.find((lang) => lang.value === current)) == null ? void 0 : _a.label) || current || "目标语言";
1095
+ });
1096
+ const selectedOptionLabel = (options, value, fallback) => {
1097
+ var _a;
1098
+ return ((_a = options.find((option) => option.value === value)) == null ? void 0 : _a.label) || fallback;
1099
+ };
1100
+ const selectedLocaleLabel = computed(
1101
+ () => selectedOptionLabel(localeOptions.value, props.manager.locale.value, "选择语言")
1102
+ );
1103
+ const selectedProviderLabel = computed(
1104
+ () => selectedOptionLabel(providerSelectOptions.value, props.manager.provider.value, "选择翻译引擎")
1105
+ );
1106
+ const selectedEntryLabel = computed(
1107
+ () => selectedOptionLabel(entryOptions.value, selectedEntryId.value, "选择片段")
1108
+ );
1109
+ const sourcePreview = (entry) => {
1110
+ const source = `${entry.source ?? ""}`.trim();
1111
+ return source.length > 320 ? `${source.slice(0, 320)}...` : source;
1112
+ };
1113
+ const entryOptionLabel = (entry) => {
1114
+ const source = sourcePreview(entry).replace(/\s+/g, " ");
1115
+ const label = fieldLabel(entry.field);
1116
+ const text = source.length > 64 ? `${source.slice(0, 64)}...` : source;
1117
+ return `${label} - ${text || entry.label || entry.key}`;
1118
+ };
1119
+ const fieldLabel = (field) => {
1120
+ const known = fieldLabels[field];
1121
+ if (known)
1122
+ return known;
1123
+ if (`${field}`.startsWith("prop:"))
1124
+ return "组件配置";
1125
+ if (`${field}`.startsWith("attr:"))
1126
+ return "属性";
1127
+ return `${field}`;
1128
+ };
1129
+ const providerLabel = (config) => {
1130
+ const engineType = `${config.engineType || ""}`;
1131
+ const engineLabel = engineLabels[engineType] || engineType || "翻译引擎";
1132
+ return config.model ? `${engineLabel} / ${config.model}` : engineLabel;
1133
+ };
1134
+ const loadProviderOptions = async () => {
1135
+ providerLoading.value = true;
1136
+ providerConfigError.value = "";
1137
+ try {
1138
+ const configs = await props.manager.loadProviderConfigs();
1139
+ providerOptions.value = configs.filter((config) => config.engineType).map((config) => ({
1140
+ key: `${config.id ?? config.engineType}-${config.model ?? ""}`,
1141
+ label: providerLabel(config),
1142
+ value: `${config.engineType}`
1143
+ }));
1144
+ if (!providerOptions.value.length) {
1145
+ props.manager.setProvider("");
1146
+ providerConfigError.value = "未启用翻译引擎,请先到翻译配置中启用";
1147
+ return;
1148
+ }
1149
+ if (!providerOptions.value.some((option) => option.value === props.manager.provider.value)) {
1150
+ props.manager.setProvider(providerOptions.value[0].value);
1151
+ }
1152
+ } catch (error) {
1153
+ providerOptions.value = [];
1154
+ props.manager.setProvider("");
1155
+ providerConfigError.value = (error == null ? void 0 : error.message) || "加载启用翻译引擎失败";
1156
+ } finally {
1157
+ providerLoading.value = false;
1158
+ }
1159
+ };
1160
+ const handleMachineTranslate = async () => {
1161
+ if (!hasEnabledProvider.value) {
1162
+ props.manager.hostUi.message.warning(providerConfigError.value || "请先启用翻译引擎");
1163
+ return;
1164
+ }
1165
+ await props.manager.machineTranslate("missing");
1166
+ if (props.manager.lastError.value) {
1167
+ props.manager.hostUi.message.warning(props.manager.lastError.value);
1168
+ }
1169
+ };
1170
+ const handleMachineTranslateActive = async () => {
1171
+ const entry = activeEntry.value;
1172
+ if (!entry)
1173
+ return;
1174
+ if (!hasEnabledProvider.value) {
1175
+ props.manager.hostUi.message.warning(providerConfigError.value || "请先启用翻译引擎");
1176
+ return;
1177
+ }
1178
+ await props.manager.machineTranslateEntries([entry]);
1179
+ if (props.manager.lastError.value) {
1180
+ props.manager.hostUi.message.warning(props.manager.lastError.value);
1181
+ }
1182
+ };
1183
+ const handleNextTodo = () => {
1184
+ const candidates = visibleActionableEntries.value.length ? visibleEntries.value : props.manager.entries.value;
1185
+ if (!candidates.length)
1186
+ return;
1187
+ const currentIndex = candidates.findIndex(
1188
+ (entry) => getWebBuilderI18nEntryId(entry) === selectedEntryId.value
1189
+ );
1190
+ const next = candidates.slice(currentIndex + 1).find(isActionableEntry) || candidates.find(isActionableEntry);
1191
+ if (!next)
1192
+ return;
1193
+ selectedEntryId.value = getWebBuilderI18nEntryId(next);
1194
+ props.manager.selectEntryComponent(next);
1195
+ };
1196
+ const handleRefresh = async () => {
1197
+ await loadProviderOptions();
1198
+ props.manager.refreshSourceEntries();
1199
+ await props.manager.loadBundle();
1200
+ };
1201
+ const toggleSelect = (selectId, disabled = false) => {
1202
+ if (disabled)
1203
+ return;
1204
+ openSelectId.value = openSelectId.value === selectId ? "" : selectId;
1205
+ };
1206
+ const closeSelect = () => {
1207
+ openSelectId.value = "";
1208
+ };
1209
+ const handleLocaleSelect = (value) => {
1210
+ if (!value || localeSelectDisabled.value)
1211
+ return;
1212
+ props.manager.setLocale(value);
1213
+ closeSelect();
1214
+ };
1215
+ const handleProviderSelect = (value) => {
1216
+ if (!value || providerSelectDisabled.value)
1217
+ return;
1218
+ props.manager.setProvider(value);
1219
+ closeSelect();
1220
+ };
1221
+ const handleEntrySelect = (value) => {
1222
+ if (!value)
1223
+ return;
1224
+ selectedEntryId.value = value;
1225
+ closeSelect();
1226
+ };
1227
+ const handleToggleNoTranslate = () => {
1228
+ const nextSkipped = !selectedNoTranslate.value;
1229
+ if (!setModelI18nSkipped(selectedComponent.value, nextSkipped))
1230
+ return;
1231
+ noTranslateRevision.value += 1;
1232
+ props.manager.refreshSourceEntries();
1233
+ props.manager.hostUi.message.success(nextSkipped ? "已设为不翻译" : "已恢复翻译");
1234
+ };
1235
+ const handleActiveTranslationInput = (event) => {
1236
+ const entry = activeEntry.value;
1237
+ if (!entry)
1238
+ return;
1239
+ props.manager.setTranslation(entry, event.target.value);
1240
+ };
1241
+ onMounted(async () => {
1242
+ await props.manager.loadLanguages();
1243
+ await loadProviderOptions();
1244
+ await props.manager.loadBundle();
1245
+ });
1246
+ watch(
1247
+ visibleEntries,
1248
+ (entries) => {
1249
+ const ids = entries.map(getWebBuilderI18nEntryId);
1250
+ if (!ids.length) {
1251
+ selectedEntryId.value = "";
1252
+ return;
1253
+ }
1254
+ if (!selectedEntryId.value || !ids.includes(selectedEntryId.value)) {
1255
+ selectedEntryId.value = ids[0];
1256
+ }
1257
+ },
1258
+ { immediate: true }
1259
+ );
1260
+ watch(
1261
+ () => {
1262
+ var _a;
1263
+ return [((_a = selectedComponent.value) == null ? void 0 : _a.cid) ?? "", selectedRevision.value];
1264
+ },
1265
+ ([cid]) => {
1266
+ if (!cid)
1267
+ return;
1268
+ props.manager.refreshSourceEntries();
1269
+ }
1270
+ );
1271
+ return (_ctx, _cache) => {
1272
+ return openBlock(), createElementBlock("aside", _hoisted_1$2, [
1273
+ createElementVNode("div", _hoisted_2$2, [
1274
+ createElementVNode("div", null, [
1275
+ _cache[3] || (_cache[3] = createElementVNode("div", { class: "wb-i18n-panel__title" }, "片段翻译", -1)),
1276
+ createElementVNode("div", _hoisted_3$2, toDisplayString(selectedOnly.value ? "当前选中组件" : "全部可翻译片段"), 1)
1277
+ ]),
1278
+ createElementVNode("button", {
1279
+ class: "wb-i18n-icon-btn",
1280
+ type: "button",
1281
+ title: "刷新",
1282
+ onClick: handleRefresh
1283
+ }, " ↻ ")
1284
+ ]),
1285
+ createElementVNode("div", _hoisted_4$2, [
1286
+ createElementVNode("div", _hoisted_5$2, [
1287
+ _cache[5] || (_cache[5] = createElementVNode("span", null, "语言", -1)),
1288
+ createElementVNode("div", {
1289
+ class: normalizeClass(["wb-i18n-select-ui", { "is-open": openSelectId.value === "locale", "is-disabled": localeSelectDisabled.value }])
1290
+ }, [
1291
+ createElementVNode("button", {
1292
+ class: "wb-i18n-select-ui__trigger",
1293
+ type: "button",
1294
+ disabled: localeSelectDisabled.value,
1295
+ "aria-haspopup": "listbox",
1296
+ "aria-expanded": openSelectId.value === "locale",
1297
+ onClick: _cache[0] || (_cache[0] = ($event) => toggleSelect("locale", localeSelectDisabled.value))
1298
+ }, [
1299
+ createElementVNode("span", _hoisted_7$2, toDisplayString(selectedLocaleLabel.value), 1),
1300
+ _cache[4] || (_cache[4] = createElementVNode("span", { class: "wb-i18n-select-ui__chevron" }, "⌄", -1))
1301
+ ], 8, _hoisted_6$2),
1302
+ openSelectId.value === "locale" ? (openBlock(), createElementBlock("div", _hoisted_8$2, [
1303
+ (openBlock(true), createElementBlock(Fragment, null, renderList(localeOptions.value, (option) => {
1304
+ return openBlock(), createElementBlock("button", {
1305
+ key: option.key,
1306
+ class: normalizeClass(["wb-i18n-select-ui__option", { "is-selected": option.value === _ctx.manager.locale.value }]),
1307
+ type: "button",
1308
+ role: "option",
1309
+ "aria-selected": option.value === _ctx.manager.locale.value,
1310
+ onClick: ($event) => handleLocaleSelect(option.value)
1311
+ }, toDisplayString(option.label), 11, _hoisted_9$2);
1312
+ }), 128))
1313
+ ])) : createCommentVNode("", true)
1314
+ ], 2)
1315
+ ]),
1316
+ createElementVNode("div", _hoisted_10$2, [
1317
+ _cache[7] || (_cache[7] = createElementVNode("span", null, "翻译引擎", -1)),
1318
+ createElementVNode("div", {
1319
+ class: normalizeClass(["wb-i18n-select-ui", { "is-open": openSelectId.value === "provider", "is-disabled": providerSelectDisabled.value }])
1320
+ }, [
1321
+ createElementVNode("button", {
1322
+ class: "wb-i18n-select-ui__trigger",
1323
+ type: "button",
1324
+ disabled: providerSelectDisabled.value,
1325
+ "aria-haspopup": "listbox",
1326
+ "aria-expanded": openSelectId.value === "provider",
1327
+ onClick: _cache[1] || (_cache[1] = ($event) => toggleSelect("provider", providerSelectDisabled.value))
1328
+ }, [
1329
+ createElementVNode("span", _hoisted_12$2, toDisplayString(selectedProviderLabel.value), 1),
1330
+ _cache[6] || (_cache[6] = createElementVNode("span", { class: "wb-i18n-select-ui__chevron" }, "⌄", -1))
1331
+ ], 8, _hoisted_11$2),
1332
+ openSelectId.value === "provider" ? (openBlock(), createElementBlock("div", _hoisted_13$2, [
1333
+ (openBlock(true), createElementBlock(Fragment, null, renderList(providerSelectOptions.value, (option) => {
1334
+ return openBlock(), createElementBlock("button", {
1335
+ key: option.key,
1336
+ class: normalizeClass(["wb-i18n-select-ui__option", { "is-selected": option.value === _ctx.manager.provider.value }]),
1337
+ type: "button",
1338
+ role: "option",
1339
+ "aria-selected": option.value === _ctx.manager.provider.value,
1340
+ onClick: ($event) => handleProviderSelect(option.value)
1341
+ }, toDisplayString(option.label), 11, _hoisted_14$2);
1342
+ }), 128))
1343
+ ])) : createCommentVNode("", true)
1344
+ ], 2),
1345
+ providerConfigError.value ? (openBlock(), createElementBlock("span", _hoisted_15$2, toDisplayString(providerConfigError.value), 1)) : createCommentVNode("", true)
1346
+ ])
1347
+ ]),
1348
+ createElementVNode("div", _hoisted_16$2, [
1349
+ createElementVNode("span", null, toDisplayString(_ctx.manager.translatedCount.value) + " 已翻译", 1),
1350
+ createElementVNode("span", null, toDisplayString(_ctx.manager.missingCount.value) + " 缺失", 1)
1351
+ ]),
1352
+ hasSelectedComponent.value ? (openBlock(), createElementBlock("button", {
1353
+ key: 0,
1354
+ class: "wb-i18n-btn wb-i18n-btn--secondary wb-i18n-no-translate-btn",
1355
+ type: "button",
1356
+ onClick: handleToggleNoTranslate
1357
+ }, toDisplayString(selectedNoTranslate.value ? "恢复翻译此组件" : "不翻译此组件"), 1)) : createCommentVNode("", true),
1358
+ _ctx.manager.lastError.value ? (openBlock(), createElementBlock("div", _hoisted_17$2, toDisplayString(_ctx.manager.lastError.value), 1)) : createCommentVNode("", true),
1359
+ createElementVNode("div", _hoisted_18$2, [
1360
+ _ctx.manager.loading.value ? (openBlock(), createElementBlock("div", _hoisted_19$2, "加载翻译 bundle...")) : selectedNoTranslate.value ? (openBlock(), createElementBlock("div", _hoisted_20$2, " 当前组件已排除翻译。 ")) : !visibleEntries.value.length ? (openBlock(), createElementBlock("div", _hoisted_21$2, " 当前项目没有可翻译文本。 ")) : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
1361
+ createElementVNode("div", _hoisted_22$2, [
1362
+ _cache[9] || (_cache[9] = createElementVNode("span", null, "片段", -1)),
1363
+ createElementVNode("div", {
1364
+ class: normalizeClass(["wb-i18n-select-ui", { "is-open": openSelectId.value === "entry" }])
1365
+ }, [
1366
+ createElementVNode("button", {
1367
+ class: "wb-i18n-select-ui__trigger",
1368
+ type: "button",
1369
+ "aria-haspopup": "listbox",
1370
+ "aria-expanded": openSelectId.value === "entry",
1371
+ onClick: _cache[2] || (_cache[2] = ($event) => toggleSelect("entry"))
1372
+ }, [
1373
+ createElementVNode("span", _hoisted_24$2, toDisplayString(selectedEntryLabel.value), 1),
1374
+ _cache[8] || (_cache[8] = createElementVNode("span", { class: "wb-i18n-select-ui__chevron" }, "⌄", -1))
1375
+ ], 8, _hoisted_23$2),
1376
+ openSelectId.value === "entry" ? (openBlock(), createElementBlock("div", _hoisted_25, [
1377
+ (openBlock(true), createElementBlock(Fragment, null, renderList(entryOptions.value, (option) => {
1378
+ return openBlock(), createElementBlock("button", {
1379
+ key: option.key,
1380
+ class: normalizeClass(["wb-i18n-select-ui__option", { "is-selected": option.value === selectedEntryId.value }]),
1381
+ type: "button",
1382
+ role: "option",
1383
+ "aria-selected": option.value === selectedEntryId.value,
1384
+ onClick: ($event) => handleEntrySelect(option.value)
1385
+ }, toDisplayString(option.label), 11, _hoisted_26);
1386
+ }), 128))
1387
+ ])) : createCommentVNode("", true)
1388
+ ], 2)
1389
+ ]),
1390
+ activeEntry.value ? (openBlock(), createElementBlock("section", _hoisted_27, [
1391
+ createElementVNode("div", _hoisted_28, [
1392
+ createElementVNode("span", _hoisted_29, toDisplayString(fieldLabel(activeEntry.value.field)), 1),
1393
+ createElementVNode("span", {
1394
+ class: normalizeClass(["wb-i18n-status", statusClass[activeEntry.value.status || "missing"]])
1395
+ }, toDisplayString(statusLabels[activeEntry.value.status || "missing"]), 3),
1396
+ dirtyIds.value.has(unref(getWebBuilderI18nEntryId)(activeEntry.value)) ? (openBlock(), createElementBlock("span", _hoisted_30, " 未保存 ")) : createCommentVNode("", true)
1397
+ ]),
1398
+ createElementVNode("div", _hoisted_31, [
1399
+ createElementVNode("label", _hoisted_32, [
1400
+ createElementVNode("span", null, "From " + toDisplayString(sourceLanguageLabel.value), 1),
1401
+ createElementVNode("textarea", {
1402
+ class: "wb-i18n-textarea wb-i18n-textarea--source",
1403
+ value: activeEntry.value.source,
1404
+ rows: "3",
1405
+ readonly: ""
1406
+ }, null, 8, _hoisted_33)
1407
+ ]),
1408
+ createElementVNode("label", _hoisted_34, [
1409
+ createElementVNode("span", null, "To " + toDisplayString(targetLanguageLabel.value), 1),
1410
+ createElementVNode("textarea", {
1411
+ class: "wb-i18n-textarea",
1412
+ value: activeEntry.value.translation,
1413
+ rows: "4",
1414
+ placeholder: "输入译文,或使用翻译引擎生成",
1415
+ onInput: handleActiveTranslationInput
1416
+ }, null, 40, _hoisted_35)
1417
+ ])
1418
+ ]),
1419
+ createElementVNode("div", _hoisted_36, [
1420
+ createElementVNode("button", {
1421
+ class: "wb-i18n-btn",
1422
+ type: "button",
1423
+ disabled: _ctx.manager.translating.value || _ctx.manager.loading.value || !activeEntry.value || !hasEnabledProvider.value,
1424
+ onClick: handleMachineTranslateActive
1425
+ }, toDisplayString(_ctx.manager.translating.value ? "翻译中..." : "AI 生成"), 9, _hoisted_37)
1426
+ ])
1427
+ ])) : createCommentVNode("", true)
1428
+ ], 64))
1429
+ ]),
1430
+ createElementVNode("div", _hoisted_38, [
1431
+ createElementVNode("button", {
1432
+ class: "wb-i18n-btn wb-i18n-btn--secondary",
1433
+ type: "button",
1434
+ disabled: _ctx.manager.translating.value || _ctx.manager.loading.value || !_ctx.manager.missingCount.value || !hasEnabledProvider.value,
1435
+ onClick: handleMachineTranslate
1436
+ }, " 自动翻译缺失项 ", 8, _hoisted_39),
1437
+ createElementVNode("button", {
1438
+ class: "wb-i18n-btn wb-i18n-btn--secondary",
1439
+ type: "button",
1440
+ disabled: !hasActionableEntries.value,
1441
+ onClick: handleNextTodo
1442
+ }, " 下一个待处理 ", 8, _hoisted_40)
1443
+ ])
1444
+ ]);
1445
+ };
1446
+ }
1447
+ });
1448
+ const I18nPanel = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-a53e606f"]]);
1449
+ const _hoisted_1$1 = { class: "layout-panel" };
1450
+ const _hoisted_2$1 = { class: "layout-panel__header" };
1451
+ const _hoisted_3$1 = { class: "layout-panel__title" };
1452
+ const _hoisted_4$1 = { class: "layout-panel__description" };
1453
+ const _hoisted_5$1 = { class: "layout-panel__actions" };
1454
+ const _hoisted_6$1 = ["disabled"];
1455
+ const _hoisted_7$1 = {
1456
+ key: 0,
1457
+ class: "layout-panel__note"
1458
+ };
1459
+ const _hoisted_8$1 = {
1460
+ key: 1,
1461
+ class: "layout-panel__note"
1462
+ };
1463
+ const _hoisted_9$1 = { class: "layout-rules" };
1464
+ const _hoisted_10$1 = ["onClick"];
1465
+ const _hoisted_11$1 = { class: "min-w-0 flex-1" };
1466
+ const _hoisted_12$1 = { class: "layout-rule-card__title" };
1467
+ const _hoisted_13$1 = { class: "layout-rule-card__meta" };
1468
+ const _hoisted_14$1 = { class: "layout-rule-card__badge" };
1469
+ const _hoisted_15$1 = {
1470
+ key: 0,
1471
+ class: "layout-rule-card__body"
1472
+ };
1473
+ const _hoisted_16$1 = { class: "layout-form-field" };
1474
+ const _hoisted_17$1 = ["value", "disabled", "onInput"];
1475
+ const _hoisted_18$1 = { class: "layout-form-row" };
1476
+ const _hoisted_19$1 = { class: "layout-form-field layout-form-field--grow" };
1477
+ const _hoisted_20$1 = { class: "layout-form-field layout-form-field--inline" };
1478
+ const _hoisted_21$1 = { class: "layout-form-field" };
1479
+ const _hoisted_22$1 = { class: "layout-rule-card__hint" };
1480
+ const _hoisted_23$1 = { class: "layout-rule-card__footer" };
1481
+ const _hoisted_24$1 = {
1482
+ key: 1,
1483
+ class: "layout-panel__empty"
1484
+ };
1485
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
1486
+ __name: "LayoutPanel",
1487
+ props: {
1488
+ pages: {},
1489
+ layoutSettings: {},
1490
+ setLayoutSettings: { type: Function },
1491
+ loadRulePageOptions: { type: Function },
1492
+ ensureRulesEditable: { type: Function },
1493
+ getRulesLockState: { type: Function }
1494
+ },
1495
+ emits: ["change"],
1496
+ setup(__props, { emit: __emit }) {
1497
+ const props = __props;
1498
+ const emit = __emit;
1499
+ const expandedRuleId = ref(null);
1500
+ const selectablePages = ref([]);
1501
+ const loadingPages = ref(false);
1502
+ const currentLayoutPage = computed(() => {
1503
+ return props.pages.find((page) => !!getPageLayoutSlot(page)) ?? null;
1504
+ });
1505
+ const currentSlot = computed(() => {
1506
+ return currentLayoutPage.value ? getPageLayoutSlot(currentLayoutPage.value) : null;
1507
+ });
1508
+ const currentLayoutId = computed(() => {
1509
+ return currentLayoutPage.value ? getGrapesPageId(currentLayoutPage.value) : "";
1510
+ });
1511
+ const currentLayoutName = computed(() => {
1512
+ return currentLayoutPage.value ? getGrapesPageName(currentLayoutPage.value) : "";
1513
+ });
1514
+ const rulesLockState = computed(() => {
1515
+ var _a;
1516
+ return ((_a = props.getRulesLockState) == null ? void 0 : _a.call(props)) ?? {};
1517
+ });
1518
+ const rulesReadonly = computed(
1519
+ () => ["conflict", "error", "acquiring"].includes(`${rulesLockState.value.status ?? ""}`)
1520
+ );
1521
+ const activeRules = computed(() => {
1522
+ const slotKey = currentSlot.value;
1523
+ const layoutPage = currentLayoutPage.value;
1524
+ if (!slotKey || !layoutPage)
1525
+ return [];
1526
+ return props.layoutSettings[slotKey].rules.filter((rule) => layoutTargetMatchesPage(rule.layoutId, layoutPage));
1527
+ });
1528
+ async function loadSelectablePages() {
1529
+ loadingPages.value = true;
1530
+ try {
1531
+ selectablePages.value = await props.loadRulePageOptions();
1532
+ } catch (error) {
1533
+ console.warn("[WebBuilder] 页面规则候选页加载失败", error);
1534
+ selectablePages.value = [];
1535
+ } finally {
1536
+ loadingPages.value = false;
1537
+ }
1538
+ }
1539
+ async function ensureRulesAccess(silent = false) {
1540
+ if (!props.ensureRulesEditable)
1541
+ return true;
1542
+ return props.ensureRulesEditable(silent);
1543
+ }
1544
+ function updateSettings(mutator) {
1545
+ const next = cloneLayoutSettings(props.layoutSettings);
1546
+ mutator(next);
1547
+ props.setLayoutSettings(next);
1548
+ emit("change");
1549
+ }
1550
+ function normalizeRuleOrder(rules) {
1551
+ rules.forEach((rule, index) => {
1552
+ rule.priority = index;
1553
+ });
1554
+ }
1555
+ function createRule(slotKey, layoutId, index) {
1556
+ return {
1557
+ id: `${slotKey}-rule-${Date.now().toString(36)}-${index + 1}`,
1558
+ name: `规则 ${index + 1}`,
1559
+ layoutId,
1560
+ matchMode: "exclude",
1561
+ pageIds: [],
1562
+ enabled: true,
1563
+ priority: index
1564
+ };
1565
+ }
1566
+ async function addRule() {
1567
+ const slotKey = currentSlot.value;
1568
+ const layoutId = currentLayoutId.value;
1569
+ if (!slotKey || !layoutId)
1570
+ return;
1571
+ if (!await ensureRulesAccess())
1572
+ return;
1573
+ let createdRuleId = "";
1574
+ updateSettings((next) => {
1575
+ const rules = next[slotKey].rules;
1576
+ const currentLayoutRules = rules.filter((rule2) => `${rule2.layoutId ?? ""}`.trim() === layoutId);
1577
+ const rule = createRule(slotKey, layoutId, currentLayoutRules.length);
1578
+ createdRuleId = rule.id;
1579
+ rules.push(rule);
1580
+ normalizeRuleOrder(rules);
1581
+ });
1582
+ expandedRuleId.value = createdRuleId;
1583
+ }
1584
+ async function removeRule(ruleId) {
1585
+ const slotKey = currentSlot.value;
1586
+ if (!slotKey)
1587
+ return;
1588
+ if (!await ensureRulesAccess())
1589
+ return;
1590
+ updateSettings((next) => {
1591
+ next[slotKey].rules = next[slotKey].rules.filter((rule) => rule.id !== ruleId);
1592
+ normalizeRuleOrder(next[slotKey].rules);
1593
+ });
1594
+ if (expandedRuleId.value === ruleId) {
1595
+ expandedRuleId.value = null;
1596
+ }
1597
+ }
1598
+ async function patchRule(ruleId, patch) {
1599
+ const slotKey = currentSlot.value;
1600
+ if (!slotKey)
1601
+ return;
1602
+ if (!await ensureRulesAccess(true))
1603
+ return;
1604
+ updateSettings((next) => {
1605
+ const rule = next[slotKey].rules.find((item) => item.id === ruleId);
1606
+ if (!rule)
1607
+ return;
1608
+ Object.assign(rule, patch);
1609
+ });
1610
+ }
1611
+ async function updateRuleName(ruleId, value) {
1612
+ await patchRule(ruleId, { name: value });
1613
+ }
1614
+ async function updateRuleMatchMode(ruleId, value) {
1615
+ await patchRule(ruleId, { matchMode: value });
1616
+ }
1617
+ async function updateRulePages(ruleId, value) {
1618
+ await patchRule(ruleId, {
1619
+ pageIds: value.map((item) => `${item ?? ""}`.trim()).filter(Boolean)
1620
+ });
1621
+ }
1622
+ async function updateRuleEnabled(ruleId, value) {
1623
+ await patchRule(ruleId, { enabled: value });
1624
+ }
1625
+ function getRuleDisplayName(rule, index) {
1626
+ var _a;
1627
+ return ((_a = rule.name) == null ? void 0 : _a.trim()) || `规则 ${index + 1}`;
1628
+ }
1629
+ function getRuleModeLabel(rule) {
1630
+ return rule.matchMode === "exclude" ? "排除页面" : "指定页面";
1631
+ }
1632
+ function getRuleScopeText(rule) {
1633
+ const selectedCount = rule.pageIds.length;
1634
+ if (rule.matchMode === "include") {
1635
+ return selectedCount > 0 ? `命中 ${selectedCount} 个页面` : "未选择页面,规则暂不生效";
1636
+ }
1637
+ return selectedCount > 0 ? `排除 ${selectedCount} 个页面` : "除已排除页面外全部命中";
1638
+ }
1639
+ function toggleRule(ruleId) {
1640
+ expandedRuleId.value = expandedRuleId.value === ruleId ? null : ruleId;
1641
+ }
1642
+ onMounted(() => {
1643
+ loadSelectablePages();
1644
+ });
1645
+ return (_ctx, _cache) => {
1646
+ return openBlock(), createElementBlock("div", _hoisted_1$1, [
1647
+ currentSlot.value && currentLayoutId.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
1648
+ createElementVNode("div", _hoisted_2$1, [
1649
+ createElementVNode("div", _hoisted_3$1, toDisplayString(currentLayoutName.value || currentLayoutId.value), 1),
1650
+ createElementVNode("div", _hoisted_4$1, toDisplayString(currentSlot.value === "header" ? "Header 规则配置" : "Footer 规则配置"), 1)
1651
+ ]),
1652
+ createElementVNode("div", _hoisted_5$1, [
1653
+ createElementVNode("button", {
1654
+ type: "button",
1655
+ class: "layout-action-button",
1656
+ disabled: rulesReadonly.value,
1657
+ onClick: addRule
1658
+ }, [
1659
+ createVNode(unref(Icon), {
1660
+ icon: "material-symbols-light:rule-rounded",
1661
+ size: 16
1662
+ }),
1663
+ _cache[1] || (_cache[1] = createElementVNode("span", null, "新增规则", -1))
1664
+ ], 8, _hoisted_6$1)
1665
+ ]),
1666
+ rulesLockState.value.message ? (openBlock(), createElementBlock("div", _hoisted_7$1, toDisplayString(rulesLockState.value.message), 1)) : activeRules.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_8$1, " 当前没有命中规则,页面不会显示该布局。添加规则后才会生效。 ")) : createCommentVNode("", true),
1667
+ createElementVNode("div", _hoisted_9$1, [
1668
+ (openBlock(true), createElementBlock(Fragment, null, renderList(activeRules.value, (rule, index) => {
1669
+ return openBlock(), createElementBlock("div", {
1670
+ key: rule.id,
1671
+ class: "layout-rule-card"
1672
+ }, [
1673
+ createElementVNode("button", {
1674
+ type: "button",
1675
+ class: "layout-rule-card__summary",
1676
+ onClick: ($event) => toggleRule(rule.id)
1677
+ }, [
1678
+ createElementVNode("div", _hoisted_11$1, [
1679
+ createElementVNode("div", _hoisted_12$1, toDisplayString(getRuleDisplayName(rule, index)), 1),
1680
+ createElementVNode("div", _hoisted_13$1, toDisplayString(getRuleScopeText(rule)), 1)
1681
+ ]),
1682
+ createElementVNode("div", _hoisted_14$1, toDisplayString(getRuleModeLabel(rule)), 1),
1683
+ createVNode(unref(Icon), {
1684
+ class: normalizeClass(["layout-rule-card__chevron", { "layout-rule-card__chevron--open": expandedRuleId.value === rule.id }]),
1685
+ icon: "lucide:chevron-down",
1686
+ size: 16
1687
+ }, null, 8, ["class"])
1688
+ ], 8, _hoisted_10$1),
1689
+ expandedRuleId.value === rule.id ? (openBlock(), createElementBlock("div", _hoisted_15$1, [
1690
+ createElementVNode("label", _hoisted_16$1, [
1691
+ _cache[2] || (_cache[2] = createElementVNode("span", { class: "layout-form-field__label" }, "规则名称", -1)),
1692
+ createElementVNode("input", {
1693
+ value: rule.name || "",
1694
+ class: "layout-input",
1695
+ type: "text",
1696
+ disabled: rulesReadonly.value,
1697
+ onInput: ($event) => updateRuleName(rule.id, $event.target.value)
1698
+ }, null, 40, _hoisted_17$1)
1699
+ ]),
1700
+ createElementVNode("div", _hoisted_18$1, [
1701
+ createElementVNode("label", _hoisted_19$1, [
1702
+ _cache[3] || (_cache[3] = createElementVNode("span", { class: "layout-form-field__label" }, "匹配模式", -1)),
1703
+ createVNode(unref(ElSelect), {
1704
+ "model-value": rule.matchMode,
1705
+ class: "w-full",
1706
+ disabled: rulesReadonly.value,
1707
+ "onUpdate:modelValue": (value) => updateRuleMatchMode(rule.id, value)
1708
+ }, {
1709
+ default: withCtx(() => [
1710
+ createVNode(unref(ElOption), {
1711
+ label: "排除选中页面,其它页面生效",
1712
+ value: "exclude"
1713
+ }),
1714
+ createVNode(unref(ElOption), {
1715
+ label: "仅选中页面生效",
1716
+ value: "include"
1717
+ })
1718
+ ]),
1719
+ _: 2
1720
+ }, 1032, ["model-value", "disabled", "onUpdate:modelValue"])
1721
+ ]),
1722
+ createElementVNode("label", _hoisted_20$1, [
1723
+ _cache[4] || (_cache[4] = createElementVNode("span", { class: "layout-form-field__label" }, "启用", -1)),
1724
+ createVNode(unref(ElSwitch), {
1725
+ "model-value": rule.enabled !== false,
1726
+ disabled: rulesReadonly.value,
1727
+ "onUpdate:modelValue": (value) => updateRuleEnabled(rule.id, Boolean(value))
1728
+ }, null, 8, ["model-value", "disabled", "onUpdate:modelValue"])
1729
+ ])
1730
+ ]),
1731
+ createElementVNode("label", _hoisted_21$1, [
1732
+ _cache[5] || (_cache[5] = createElementVNode("span", { class: "layout-form-field__label" }, "页面多选", -1)),
1733
+ createVNode(unref(ElSelect), {
1734
+ "model-value": rule.pageIds,
1735
+ class: "w-full",
1736
+ multiple: "",
1737
+ filterable: "",
1738
+ clearable: "",
1739
+ disabled: rulesReadonly.value || loadingPages.value,
1740
+ placeholder: "选择页面",
1741
+ "onUpdate:modelValue": (value) => updateRulePages(rule.id, value)
1742
+ }, {
1743
+ default: withCtx(() => [
1744
+ (openBlock(true), createElementBlock(Fragment, null, renderList(selectablePages.value, (option) => {
1745
+ return openBlock(), createBlock(unref(ElOption), {
1746
+ key: option.id,
1747
+ label: option.label,
1748
+ value: option.id
1749
+ }, null, 8, ["label", "value"]);
1750
+ }), 128))
1751
+ ]),
1752
+ _: 2
1753
+ }, 1032, ["model-value", "disabled", "onUpdate:modelValue"])
1754
+ ]),
1755
+ createElementVNode("div", _hoisted_22$1, [
1756
+ rule.matchMode === "exclude" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
1757
+ createTextVNode(" 排除:除勾选页面外全部生效。不勾选页面时,表示全站生效。 ")
1758
+ ], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
1759
+ createTextVNode(" 指定:仅在勾选页面生效。 ")
1760
+ ], 64))
1761
+ ]),
1762
+ createElementVNode("div", _hoisted_23$1, [
1763
+ createVNode(unref(ElPopconfirm), {
1764
+ title: "确定删除这条规则?",
1765
+ "confirm-button-text": "删除",
1766
+ "cancel-button-text": "取消",
1767
+ onConfirm: ($event) => removeRule(rule.id)
1768
+ }, {
1769
+ reference: withCtx(() => [
1770
+ createElementVNode("button", {
1771
+ type: "button",
1772
+ class: "layout-action-button layout-action-button--danger",
1773
+ onClick: _cache[0] || (_cache[0] = withModifiers(() => {
1774
+ }, ["stop"]))
1775
+ }, [
1776
+ createVNode(unref(Icon), {
1777
+ icon: "iconamoon:trash-thin",
1778
+ size: 16
1779
+ }),
1780
+ _cache[6] || (_cache[6] = createElementVNode("span", null, "删除规则", -1))
1781
+ ])
1782
+ ]),
1783
+ _: 2
1784
+ }, 1032, ["onConfirm"])
1785
+ ])
1786
+ ])) : createCommentVNode("", true)
1787
+ ]);
1788
+ }), 128))
1789
+ ])
1790
+ ], 64)) : (openBlock(), createElementBlock("div", _hoisted_24$1, " 当前布局页面尚未就绪。 "))
1791
+ ]);
1792
+ };
1793
+ }
1794
+ });
1795
+ const LayoutPanel = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-558d1955"]]);
1796
+ const _hoisted_1 = { class: "layout-panel" };
1797
+ const _hoisted_2 = { class: "layout-panel__actions" };
1798
+ const _hoisted_3 = ["disabled"];
1799
+ const _hoisted_4 = {
1800
+ key: 0,
1801
+ class: "layout-panel__note"
1802
+ };
1803
+ const _hoisted_5 = {
1804
+ key: 1,
1805
+ class: "layout-panel__note"
1806
+ };
1807
+ const _hoisted_6 = { class: "layout-rules" };
1808
+ const _hoisted_7 = ["onClick"];
1809
+ const _hoisted_8 = { class: "min-w-0 flex-1" };
1810
+ const _hoisted_9 = { class: "layout-rule-card__title" };
1811
+ const _hoisted_10 = { class: "layout-rule-card__meta" };
1812
+ const _hoisted_11 = { class: "layout-rule-card__badge" };
1813
+ const _hoisted_12 = {
1814
+ key: 0,
1815
+ class: "layout-rule-card__body"
1816
+ };
1817
+ const _hoisted_13 = { class: "layout-form-field" };
1818
+ const _hoisted_14 = ["value", "disabled", "onInput"];
1819
+ const _hoisted_15 = { class: "layout-form-row" };
1820
+ const _hoisted_16 = { class: "layout-form-field layout-form-field--grow" };
1821
+ const _hoisted_17 = { class: "layout-form-field layout-form-field--inline" };
1822
+ const _hoisted_18 = {
1823
+ key: 0,
1824
+ class: "layout-form-field"
1825
+ };
1826
+ const _hoisted_19 = { class: "layout-form-field__label" };
1827
+ const _hoisted_20 = {
1828
+ key: 1,
1829
+ class: "layout-form-field"
1830
+ };
1831
+ const _hoisted_21 = { class: "layout-form-field__label" };
1832
+ const _hoisted_22 = { class: "layout-rule-card__footer" };
1833
+ const _hoisted_23 = ["disabled"];
1834
+ const _hoisted_24 = {
1835
+ key: 1,
1836
+ class: "layout-panel__empty"
1837
+ };
1838
+ const _sfc_main = /* @__PURE__ */ defineComponent({
1839
+ __name: "TemplateRulesPanel",
1840
+ props: {
1841
+ currentTemplateType: {},
1842
+ currentRules: {},
1843
+ ensureRulesEditable: { type: Function },
1844
+ getRulesLockState: { type: Function },
1845
+ addRule: { type: Function },
1846
+ patchRule: { type: Function },
1847
+ removeRule: { type: Function },
1848
+ getFieldOptions: { type: Function }
1849
+ },
1850
+ setup(__props) {
1851
+ const props = __props;
1852
+ const expandedRuleId = ref(null);
1853
+ const rulesLockState = computed(() => {
1854
+ var _a;
1855
+ return ((_a = props.getRulesLockState) == null ? void 0 : _a.call(props)) ?? {};
1856
+ });
1857
+ const rulesReadonly = computed(
1858
+ () => ["conflict", "error", "acquiring"].includes(`${rulesLockState.value.status ?? ""}`)
1859
+ );
1860
+ const conditionFieldMap = {
1861
+ TEMP_POST_DETAIL: [
1862
+ { key: "postIds", label: "文章 ID", kind: "numbers", placeholder: "输入文章 ID" },
1863
+ { key: "excludePostIds", label: "排除文章 ID", kind: "numbers", placeholder: "输入不包含的文章 ID" },
1864
+ { key: "typeIds", label: "文章类型 ID", kind: "numbers", placeholder: "输入文章类型 ID" },
1865
+ { key: "excludeTypeIds", label: "排除类型 ID", kind: "numbers", placeholder: "输入不包含的文章类型 ID" },
1866
+ { key: "tagIds", label: "标签 ID", kind: "numbers", placeholder: "输入标签 ID" },
1867
+ { key: "templateNames", label: "模板名", kind: "strings", placeholder: "输入模板名" },
1868
+ { key: "publishTimeRange", label: "发布时间范围", kind: "daterange", placeholder: "" }
1869
+ ],
1870
+ TEMP_POST_CATEGORY_LIST: [
1871
+ { key: "categoryIds", label: "文章分类 ID", kind: "numbers", placeholder: "输入分类 ID" },
1872
+ { key: "excludeCategoryIds", label: "排除分类 ID", kind: "numbers", placeholder: "输入不包含的分类 ID" },
1873
+ { key: "rootCategoryIds", label: "根分类 ID", kind: "numbers", placeholder: "输入根分类 ID" },
1874
+ { key: "levels", label: "分类层级", kind: "numbers", placeholder: "输入层级" }
1875
+ ],
1876
+ TEMP_MEDIA_DETAIL: [
1877
+ { key: "resourceIds", label: "媒体资源 ID", kind: "numbers", placeholder: "输入媒体资源 ID" },
1878
+ { key: "excludeResourceIds", label: "排除媒体资源 ID", kind: "numbers", placeholder: "输入不包含的媒体资源 ID" },
1879
+ { key: "categoryIds", label: "媒体分类 ID", kind: "numbers", placeholder: "输入分类 ID" },
1880
+ { key: "excludeCategoryIds", label: "排除分类 ID", kind: "numbers", placeholder: "输入不包含的分类 ID" },
1881
+ { key: "mediaTypes", label: "媒体类型", kind: "strings", placeholder: "输入媒体类型" },
1882
+ { key: "publishTimeRange", label: "发布时间范围", kind: "daterange", placeholder: "" }
1883
+ ],
1884
+ TEMP_MEDIA_CATEGORY_LIST: [
1885
+ { key: "categoryIds", label: "媒体分类 ID", kind: "numbers", placeholder: "输入分类 ID" },
1886
+ { key: "excludeCategoryIds", label: "排除分类 ID", kind: "numbers", placeholder: "输入不包含的分类 ID" }
1887
+ ],
1888
+ TEMP_PRODUCT_DETAIL: [
1889
+ { key: "spuIds", label: "产品 SPU ID", kind: "numbers", placeholder: "输入 SPU ID" },
1890
+ { key: "excludeSpuIds", label: "排除产品 SPU ID", kind: "numbers", placeholder: "输入不包含的 SPU ID" },
1891
+ { key: "categoryIds", label: "产品分类 ID", kind: "numbers", placeholder: "输入分类 ID" },
1892
+ { key: "excludeCategoryIds", label: "排除分类 ID", kind: "numbers", placeholder: "输入不包含的分类 ID" },
1893
+ { key: "brandIds", label: "品牌 ID", kind: "numbers", placeholder: "输入品牌 ID" },
1894
+ { key: "createTimeRange", label: "创建时间范围", kind: "daterange", placeholder: "" }
1895
+ ],
1896
+ TEMP_PRODUCT_CATEGORY_LIST: [
1897
+ { key: "categoryIds", label: "分类 ID", kind: "numbers", placeholder: "输入分类 ID" },
1898
+ { key: "excludeCategoryIds", label: "排除分类 ID", kind: "numbers", placeholder: "输入不包含的分类 ID" },
1899
+ { key: "rootCategoryIds", label: "根分类 ID", kind: "numbers", placeholder: "输入根分类 ID" },
1900
+ { key: "levels", label: "分类层级", kind: "numbers", placeholder: "输入层级" }
1901
+ ]
1902
+ };
1903
+ const activeFields = computed(
1904
+ () => props.currentTemplateType && props.currentTemplateType in conditionFieldMap ? conditionFieldMap[props.currentTemplateType] : []
1905
+ );
1906
+ const isFixedOptionField = (fieldKey) => fieldKey === "levels";
1907
+ const ensureAccess = async (silent = false) => {
1908
+ if (!props.ensureRulesEditable)
1909
+ return true;
1910
+ return props.ensureRulesEditable(silent);
1911
+ };
1912
+ const toggleRule = (ruleId) => {
1913
+ expandedRuleId.value = expandedRuleId.value === ruleId ? null : ruleId;
1914
+ };
1915
+ const getRuleDisplayName = (rule, index) => {
1916
+ var _a;
1917
+ return ((_a = rule.name) == null ? void 0 : _a.trim()) || `规则 ${index + 1}`;
1918
+ };
1919
+ const getConditionSummary = (rule) => {
1920
+ const conditions = rule.conditions || {};
1921
+ const entries = Object.entries(conditions).filter(([, value]) => {
1922
+ if (Array.isArray(value))
1923
+ return value.length > 0;
1924
+ if (value && typeof value === "object") {
1925
+ return !!(`${value.start ?? ""}`.trim() || `${value.end ?? ""}`.trim());
1926
+ }
1927
+ return false;
1928
+ });
1929
+ if (!entries.length)
1930
+ return "空条件,作为兜底规则";
1931
+ return `已配置 ${entries.length} 组匹配条件`;
1932
+ };
1933
+ const updateRuleName = async (ruleId, value) => {
1934
+ var _a;
1935
+ await ((_a = props.patchRule) == null ? void 0 : _a.call(props, ruleId, { name: value }));
1936
+ };
1937
+ const updateRuleEnabled = async (ruleId, value) => {
1938
+ var _a;
1939
+ await ((_a = props.patchRule) == null ? void 0 : _a.call(props, ruleId, { enabled: value }));
1940
+ };
1941
+ const updateRulePriority = async (ruleId, value) => {
1942
+ var _a;
1943
+ await ((_a = props.patchRule) == null ? void 0 : _a.call(props, ruleId, { priority: Number.isFinite(Number(value)) ? Number(value) : 0 }));
1944
+ };
1945
+ const buildNextConditions = (rule, key, value) => ({
1946
+ ...rule.conditions || {},
1947
+ [key]: value
1948
+ });
1949
+ const updateNumberTags = async (rule, key, values) => {
1950
+ var _a;
1951
+ if (!await ensureAccess(true))
1952
+ return;
1953
+ const nextValues = values.map((item) => Number(item)).filter((item) => Number.isFinite(item));
1954
+ await ((_a = props.patchRule) == null ? void 0 : _a.call(props, rule.id, {
1955
+ conditions: buildNextConditions(rule, key, nextValues)
1956
+ }));
1957
+ };
1958
+ const updateStringTags = async (rule, key, values) => {
1959
+ var _a;
1960
+ if (!await ensureAccess(true))
1961
+ return;
1962
+ const nextValues = values.map((item) => `${item ?? ""}`.trim()).filter(Boolean);
1963
+ await ((_a = props.patchRule) == null ? void 0 : _a.call(props, rule.id, {
1964
+ conditions: buildNextConditions(rule, key, nextValues)
1965
+ }));
1966
+ };
1967
+ const updateDateRange = async (rule, key, range) => {
1968
+ var _a;
1969
+ if (!await ensureAccess(true))
1970
+ return;
1971
+ const nextRange = range ? {
1972
+ start: `${range[0] ?? ""}`.trim(),
1973
+ end: `${range[1] ?? ""}`.trim()
1974
+ } : void 0;
1975
+ await ((_a = props.patchRule) == null ? void 0 : _a.call(props, rule.id, {
1976
+ conditions: buildNextConditions(rule, key, nextRange)
1977
+ }));
1978
+ };
1979
+ const getDateRangeValue = (rule, key) => {
1980
+ var _a;
1981
+ const range = (_a = rule.conditions) == null ? void 0 : _a[key];
1982
+ if (!(range == null ? void 0 : range.start) && !(range == null ? void 0 : range.end))
1983
+ return null;
1984
+ return [range.start || "", range.end || ""];
1985
+ };
1986
+ const mergeFieldOptions = (fieldKey, rule) => {
1987
+ var _a;
1988
+ const fetched = props.getFieldOptions ? props.getFieldOptions(props.currentTemplateType, fieldKey) : [];
1989
+ const known = /* @__PURE__ */ new Map();
1990
+ fetched.forEach((option) => {
1991
+ known.set(`${option.value}`, option);
1992
+ });
1993
+ const selected = ((_a = rule.conditions) == null ? void 0 : _a[fieldKey]) || [];
1994
+ selected.forEach((value) => {
1995
+ const key = `${value}`;
1996
+ if (!known.has(key)) {
1997
+ known.set(key, { value, label: `${value}` });
1998
+ }
1999
+ });
2000
+ return Array.from(known.values());
2001
+ };
2002
+ return (_ctx, _cache) => {
2003
+ return openBlock(), createElementBlock("div", _hoisted_1, [
2004
+ _ctx.currentTemplateType ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
2005
+ _cache[8] || (_cache[8] = createElementVNode("div", { class: "layout-panel__header" }, [
2006
+ createElementVNode("div", { class: "layout-panel__title" }, "模板规则"),
2007
+ createElementVNode("div", { class: "layout-panel__description" }, "当前模板的命中规则配置")
2008
+ ], -1)),
2009
+ createElementVNode("div", _hoisted_2, [
2010
+ createElementVNode("button", {
2011
+ type: "button",
2012
+ class: "layout-action-button",
2013
+ disabled: rulesReadonly.value,
2014
+ onClick: _cache[0] || (_cache[0] = ($event) => {
2015
+ var _a;
2016
+ return (_a = _ctx.addRule) == null ? void 0 : _a.call(_ctx);
2017
+ })
2018
+ }, [
2019
+ createVNode(unref(Icon), {
2020
+ icon: "lucide:plus",
2021
+ size: 16
2022
+ }),
2023
+ _cache[2] || (_cache[2] = createElementVNode("span", null, "新增规则", -1))
2024
+ ], 8, _hoisted_3)
2025
+ ]),
2026
+ rulesLockState.value.message ? (openBlock(), createElementBlock("div", _hoisted_4, toDisplayString(rulesLockState.value.message), 1)) : _ctx.currentRules.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_5, " 当前模板还没有规则,未命中时不会参与渲染。 ")) : createCommentVNode("", true),
2027
+ createElementVNode("div", _hoisted_6, [
2028
+ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.currentRules, (rule, index) => {
2029
+ return openBlock(), createElementBlock("div", {
2030
+ key: rule.id,
2031
+ class: "layout-rule-card"
2032
+ }, [
2033
+ createElementVNode("button", {
2034
+ type: "button",
2035
+ class: "layout-rule-card__summary",
2036
+ onClick: ($event) => toggleRule(rule.id)
2037
+ }, [
2038
+ createElementVNode("div", _hoisted_8, [
2039
+ createElementVNode("div", _hoisted_9, toDisplayString(getRuleDisplayName(rule, index)), 1),
2040
+ createElementVNode("div", _hoisted_10, toDisplayString(getConditionSummary(rule)), 1)
2041
+ ]),
2042
+ createElementVNode("div", _hoisted_11, " 优先级 " + toDisplayString(rule.priority ?? 0), 1),
2043
+ createVNode(unref(Icon), {
2044
+ class: normalizeClass(["layout-rule-card__chevron", { "layout-rule-card__chevron--open": expandedRuleId.value === rule.id }]),
2045
+ icon: "lucide:chevron-down",
2046
+ size: 16
2047
+ }, null, 8, ["class"])
2048
+ ], 8, _hoisted_7),
2049
+ expandedRuleId.value === rule.id ? (openBlock(), createElementBlock("div", _hoisted_12, [
2050
+ createElementVNode("label", _hoisted_13, [
2051
+ _cache[3] || (_cache[3] = createElementVNode("span", { class: "layout-form-field__label" }, "规则名称", -1)),
2052
+ createElementVNode("input", {
2053
+ value: rule.name || "",
2054
+ class: "layout-input",
2055
+ type: "text",
2056
+ disabled: rulesReadonly.value,
2057
+ onInput: ($event) => updateRuleName(rule.id, $event.target.value)
2058
+ }, null, 40, _hoisted_14)
2059
+ ]),
2060
+ createElementVNode("div", _hoisted_15, [
2061
+ createElementVNode("label", _hoisted_16, [
2062
+ _cache[4] || (_cache[4] = createElementVNode("span", { class: "layout-form-field__label" }, "优先级", -1)),
2063
+ createVNode(unref(ElInputNumber), {
2064
+ "model-value": rule.priority,
2065
+ class: "w-full",
2066
+ min: 0,
2067
+ step: 10,
2068
+ disabled: rulesReadonly.value,
2069
+ "onUpdate:modelValue": (value) => updateRulePriority(rule.id, value)
2070
+ }, null, 8, ["model-value", "disabled", "onUpdate:modelValue"])
2071
+ ]),
2072
+ createElementVNode("label", _hoisted_17, [
2073
+ _cache[5] || (_cache[5] = createElementVNode("span", { class: "layout-form-field__label" }, "启用", -1)),
2074
+ createVNode(unref(ElSwitch), {
2075
+ "model-value": rule.enabled !== false,
2076
+ disabled: rulesReadonly.value,
2077
+ "onUpdate:modelValue": (value) => updateRuleEnabled(rule.id, Boolean(value))
2078
+ }, null, 8, ["model-value", "disabled", "onUpdate:modelValue"])
2079
+ ])
2080
+ ]),
2081
+ (openBlock(true), createElementBlock(Fragment, null, renderList(activeFields.value, (field) => {
2082
+ var _a;
2083
+ return openBlock(), createElementBlock(Fragment, {
2084
+ key: field.key
2085
+ }, [
2086
+ field.kind !== "daterange" ? (openBlock(), createElementBlock("label", _hoisted_18, [
2087
+ createElementVNode("span", _hoisted_19, toDisplayString(field.label), 1),
2088
+ createVNode(unref(ElSelect), {
2089
+ "model-value": ((_a = rule.conditions) == null ? void 0 : _a[field.key]) || [],
2090
+ class: "w-full",
2091
+ multiple: "",
2092
+ filterable: "",
2093
+ "allow-create": !isFixedOptionField(field.key),
2094
+ "default-first-option": "",
2095
+ "reserve-keyword": false,
2096
+ disabled: rulesReadonly.value,
2097
+ placeholder: field.placeholder,
2098
+ "onUpdate:modelValue": (value) => field.kind === "numbers" ? updateNumberTags(rule, field.key, value) : updateStringTags(rule, field.key, value)
2099
+ }, {
2100
+ default: withCtx(() => [
2101
+ (openBlock(true), createElementBlock(Fragment, null, renderList(mergeFieldOptions(field.key, rule), (item) => {
2102
+ return openBlock(), createBlock(unref(ElOption), {
2103
+ key: `${field.key}-${item.value}`,
2104
+ label: item.label,
2105
+ value: item.value
2106
+ }, null, 8, ["label", "value"]);
2107
+ }), 128))
2108
+ ]),
2109
+ _: 2
2110
+ }, 1032, ["model-value", "allow-create", "disabled", "placeholder", "onUpdate:modelValue"])
2111
+ ])) : (openBlock(), createElementBlock("label", _hoisted_20, [
2112
+ createElementVNode("span", _hoisted_21, toDisplayString(field.label), 1),
2113
+ createVNode(unref(ElDatePicker), {
2114
+ "model-value": getDateRangeValue(rule, field.key),
2115
+ class: "w-full",
2116
+ type: "datetimerange",
2117
+ "value-format": "YYYY-MM-DD HH:mm:ss",
2118
+ "range-separator": "至",
2119
+ "start-placeholder": "开始时间",
2120
+ "end-placeholder": "结束时间",
2121
+ disabled: rulesReadonly.value,
2122
+ "onUpdate:modelValue": (value) => updateDateRange(rule, field.key, value)
2123
+ }, null, 8, ["model-value", "disabled", "onUpdate:modelValue"])
2124
+ ]))
2125
+ ], 64);
2126
+ }), 128)),
2127
+ _cache[7] || (_cache[7] = createElementVNode("div", { class: "layout-rule-card__hint" }, " 条件之间为 AND,同一字段内多个值为 OR;`排除*` 字段命中任一值即视为不匹配。若条件为空,则作为该模板类型的兜底规则。 ", -1)),
2128
+ createElementVNode("div", _hoisted_22, [
2129
+ createVNode(unref(ElPopconfirm), {
2130
+ title: "确认删除当前规则?",
2131
+ "confirm-button-text": "删除",
2132
+ "cancel-button-text": "取消",
2133
+ onConfirm: ($event) => {
2134
+ var _a;
2135
+ return (_a = _ctx.removeRule) == null ? void 0 : _a.call(_ctx, rule.id);
2136
+ }
2137
+ }, {
2138
+ reference: withCtx(() => [
2139
+ createElementVNode("button", {
2140
+ type: "button",
2141
+ class: "layout-action-button layout-action-button--danger",
2142
+ disabled: rulesReadonly.value,
2143
+ onClick: _cache[1] || (_cache[1] = withModifiers(() => {
2144
+ }, ["stop"]))
2145
+ }, [
2146
+ createVNode(unref(Icon), {
2147
+ icon: "lucide:trash-2",
2148
+ size: 16
2149
+ }),
2150
+ _cache[6] || (_cache[6] = createElementVNode("span", null, "删除规则", -1))
2151
+ ], 8, _hoisted_23)
2152
+ ]),
2153
+ _: 2
2154
+ }, 1032, ["onConfirm"])
2155
+ ])
2156
+ ])) : createCommentVNode("", true)
2157
+ ]);
2158
+ }), 128))
2159
+ ])
2160
+ ], 64)) : (openBlock(), createElementBlock("div", _hoisted_24, " 当前资源不支持模板规则配置。 "))
2161
+ ]);
2162
+ };
2163
+ }
2164
+ });
2165
+ const TemplateRulesPanel = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-033df7a7"]]);
2166
+ export {
2167
+ FontManagerPanel,
2168
+ I18nPanel,
2169
+ LayoutPanel,
2170
+ TemplateRulesPanel
2171
+ };