@hlw-uni/mp-vue 1.0.33 → 1.0.35

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.
@@ -0,0 +1,9 @@
1
+ export type FontScale = "small" | "normal" | "large" | "xlarge";
2
+ export interface FontPreset {
3
+ label: string;
4
+ vars: Record<string, string>;
5
+ }
6
+ export declare const FONT_SCALE_KEY = "hlw_font_scale";
7
+ export declare const FONT_PRESETS: Record<FontScale, FontPreset>;
8
+ export declare function getCurrentFontScale(): FontScale;
9
+ export declare function getCurrentFontVars(): Record<string, string>;
@@ -0,0 +1,9 @@
1
+ export declare const THEME_CHANGE_EVENT = "hlw:theme-change";
2
+ export declare function buildThemeStyle(): string;
3
+ export declare function useThemePageStyle(): {
4
+ themePageStyle: any;
5
+ };
6
+ export type { FontScale, FontPreset } from './font';
7
+ export { FONT_SCALE_KEY, FONT_PRESETS, getCurrentFontScale, getCurrentFontVars } from './font';
8
+ export type { ThemeColor } from './palette';
9
+ export { THEME_COLOR_KEY, DEFAULT_THEMES, getCurrentThemeColor, getCurrentThemeVars } from './palette';
@@ -0,0 +1,8 @@
1
+ export interface ThemeColor {
2
+ label: string;
3
+ value: string;
4
+ }
5
+ export declare const THEME_COLOR_KEY = "hlw_theme_color";
6
+ export declare const DEFAULT_THEMES: ThemeColor[];
7
+ export declare function getCurrentThemeColor(): string;
8
+ export declare function getCurrentThemeVars(): Record<string, string>;
package/dist/index.d.ts CHANGED
@@ -10,5 +10,6 @@ export { default as HlwLoading } from './components/hlw-loading/index.vue';
10
10
  export { default as HlwMenu } from './components/hlw-menu/index.vue';
11
11
  export type { HlwMenuItem } from './components/hlw-menu/types';
12
12
  export { default as HlwPage } from './components/hlw-page/index.vue';
13
- export type { FontScale, FontPreset } from './components/hlw-page/font-presets';
14
- export { FONT_PRESETS, FONT_SCALE_KEY, FONT_SCALE_EVENT, getCurrentFontScale, getCurrentFontStyle, useFontPageStyle } from './components/hlw-page/font-presets';
13
+ export type { FontScale, FontPreset, ThemeColor } from './composables/theme';
14
+ export { FONT_PRESETS, FONT_SCALE_KEY, DEFAULT_THEMES, THEME_COLOR_KEY, THEME_CHANGE_EVENT, getCurrentFontScale, getCurrentFontVars, getCurrentThemeColor, getCurrentThemeVars, buildThemeStyle, useThemePageStyle } from './composables/theme';
15
+ export { useThemeStore } from './stores/theme';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue")) : typeof define === "function" && define.amd ? define(["exports", "vue"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.HlwUniVue = {}, global.vue));
3
- })(this, function(exports2, vue) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("vue"), require("@hlw-uni/mp-core"), require("pinia")) : typeof define === "function" && define.amd ? define(["exports", "vue", "@hlw-uni/mp-core", "pinia"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.HlwUniVue = {}, global.vue, global.mpCore, global.pinia));
3
+ })(this, function(exports2, vue, mpCore, pinia) {
4
4
  "use strict";
5
5
  const _sfc_main$7 = /* @__PURE__ */ vue.defineComponent({
6
6
  ...{ name: "HlwAd" },
@@ -79,67 +79,108 @@
79
79
  return target;
80
80
  };
81
81
  const index$6 = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["__scopeId", "data-v-89dcbc96"]]);
82
- const _hoisted_1$5 = { class: "hlw-card" };
83
- const _hoisted_2$5 = {
82
+ const _hoisted_1$5 = {
84
83
  key: 0,
85
84
  class: "hlw-card-header"
86
85
  };
87
- const _hoisted_3$3 = { class: "flex items-center justify-between px-5 py-4 border-0 border-b border-dashed border-slate-200 bg-slate-50/30" };
88
- const _hoisted_4$2 = { key: 0 };
89
- const _hoisted_5$1 = { class: "text-sm font-bold text-slate-800 flex items-center gap-2 tracking-wide" };
90
- const _hoisted_6$1 = { key: 1 };
86
+ const _hoisted_2$5 = { class: "hlw-card-header-inner" };
87
+ const _hoisted_3$3 = { class: "hlw-card-header-left" };
88
+ const _hoisted_4$2 = {
89
+ key: 0,
90
+ class: "hlw-card-title"
91
+ };
92
+ const _hoisted_5$1 = {
93
+ key: 0,
94
+ class: "hlw-card-header-right"
95
+ };
96
+ const _hoisted_6$1 = {
97
+ key: 0,
98
+ class: "hlw-card-extra"
99
+ };
91
100
  const _hoisted_7$1 = {
101
+ key: 1,
102
+ class: "hlw-card-divider"
103
+ };
104
+ const _hoisted_8$1 = {
105
+ key: 2,
106
+ class: "hlw-card-footer"
107
+ };
108
+ const _hoisted_9$1 = { class: "hlw-card-footer-inner" };
109
+ const _hoisted_10$1 = { class: "hlw-card-footer-left" };
110
+ const _hoisted_11$1 = {
92
111
  key: 0,
93
- class: "text-[10px] text-slate-400 bg-slate-50 px-2 py-1 rounded border border-slate-100 tracking-wide"
112
+ class: "hlw-card-footer-right"
94
113
  };
95
- const _hoisted_8$1 = { class: "hlw-card-body" };
96
114
  const _sfc_main$5 = /* @__PURE__ */ vue.defineComponent({
97
- ...{
98
- options: {
99
- styleIsolation: "shared",
100
- virtualHost: true
101
- }
102
- },
103
115
  __name: "index",
104
116
  props: {
105
117
  title: { default: "" },
106
- icon: { default: "" },
107
- iconColor: { default: "" },
108
- extra: { default: "" }
118
+ extra: { default: "" },
119
+ border: { type: Boolean, default: true },
120
+ radius: { default: "xl" },
121
+ divider: { type: Boolean, default: void 0 },
122
+ padding: { type: Boolean, default: true }
109
123
  },
110
124
  setup(__props) {
125
+ const props = __props;
126
+ const slots = vue.useSlots();
127
+ const hasHeader = vue.computed(
128
+ () => !!(props.title || props.extra || slots.header || slots["header-left"] || slots["header-right"])
129
+ );
130
+ const hasFooter = vue.computed(
131
+ () => !!(slots.footer || slots["footer-left"] || slots["footer-right"])
132
+ );
133
+ const showDivider = vue.computed(() => {
134
+ if (props.divider !== void 0)
135
+ return props.divider;
136
+ return hasHeader.value;
137
+ });
111
138
  return (_ctx, _cache) => {
112
- return vue.openBlock(), vue.createElementBlock("view", _hoisted_1$5, [
113
- _ctx.$slots.header || __props.title || __props.icon || _ctx.$slots["header-left"] || _ctx.$slots["header-right"] ? (vue.openBlock(), vue.createElementBlock("view", _hoisted_2$5, [
139
+ return vue.openBlock(), vue.createElementBlock("view", {
140
+ class: vue.normalizeClass(["hlw-card", [
141
+ `hlw-card--radius-${__props.radius}`,
142
+ __props.border ? "hlw-card--bordered" : ""
143
+ ]])
144
+ }, [
145
+ hasHeader.value ? (vue.openBlock(), vue.createElementBlock("view", _hoisted_1$5, [
114
146
  vue.renderSlot(_ctx.$slots, "header", {}, () => [
115
- vue.createElementVNode("view", _hoisted_3$3, [
116
- _ctx.$slots["header-left"] || __props.title || __props.icon ? (vue.openBlock(), vue.createElementBlock("view", _hoisted_4$2, [
147
+ vue.createElementVNode("view", _hoisted_2$5, [
148
+ vue.createElementVNode("view", _hoisted_3$3, [
117
149
  vue.renderSlot(_ctx.$slots, "header-left", {}, () => [
118
- vue.createElementVNode("view", _hoisted_5$1, [
119
- __props.icon ? (vue.openBlock(), vue.createElementBlock("text", {
120
- key: 0,
121
- class: vue.normalizeClass([__props.icon, __props.iconColor])
122
- }, null, 2)) : vue.createCommentVNode("", true),
123
- vue.createElementVNode("text", null, vue.toDisplayString(__props.title), 1)
124
- ])
150
+ __props.title ? (vue.openBlock(), vue.createElementBlock("text", _hoisted_4$2, vue.toDisplayString(__props.title), 1)) : vue.createCommentVNode("", true)
125
151
  ], true)
126
- ])) : vue.createCommentVNode("", true),
127
- _ctx.$slots["header-right"] || __props.extra ? (vue.openBlock(), vue.createElementBlock("view", _hoisted_6$1, [
152
+ ]),
153
+ _ctx.$slots["header-right"] || __props.extra ? (vue.openBlock(), vue.createElementBlock("view", _hoisted_5$1, [
128
154
  vue.renderSlot(_ctx.$slots, "header-right", {}, () => [
129
- __props.extra ? (vue.openBlock(), vue.createElementBlock("text", _hoisted_7$1, vue.toDisplayString(__props.extra), 1)) : vue.createCommentVNode("", true)
155
+ __props.extra ? (vue.openBlock(), vue.createElementBlock("text", _hoisted_6$1, vue.toDisplayString(__props.extra), 1)) : vue.createCommentVNode("", true)
130
156
  ], true)
131
157
  ])) : vue.createCommentVNode("", true)
132
158
  ])
133
159
  ], true)
134
160
  ])) : vue.createCommentVNode("", true),
135
- vue.createElementVNode("view", _hoisted_8$1, [
161
+ showDivider.value ? (vue.openBlock(), vue.createElementBlock("view", _hoisted_7$1)) : vue.createCommentVNode("", true),
162
+ vue.createElementVNode("view", {
163
+ class: vue.normalizeClass(["hlw-card-body", { "hlw-card-body--padded": __props.padding }])
164
+ }, [
136
165
  vue.renderSlot(_ctx.$slots, "default", {}, void 0, true)
137
- ])
138
- ]);
166
+ ], 2),
167
+ hasFooter.value ? (vue.openBlock(), vue.createElementBlock("view", _hoisted_8$1, [
168
+ vue.renderSlot(_ctx.$slots, "footer", {}, () => [
169
+ vue.createElementVNode("view", _hoisted_9$1, [
170
+ vue.createElementVNode("view", _hoisted_10$1, [
171
+ vue.renderSlot(_ctx.$slots, "footer-left", {}, void 0, true)
172
+ ]),
173
+ _ctx.$slots["footer-right"] ? (vue.openBlock(), vue.createElementBlock("view", _hoisted_11$1, [
174
+ vue.renderSlot(_ctx.$slots, "footer-right", {}, void 0, true)
175
+ ])) : vue.createCommentVNode("", true)
176
+ ])
177
+ ], true)
178
+ ])) : vue.createCommentVNode("", true)
179
+ ], 2);
139
180
  };
140
181
  }
141
182
  });
142
- const index$5 = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-787fc3a7"]]);
183
+ const index$5 = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-cf55252e"]]);
143
184
  const _hoisted_1$4 = { class: "hlw-empty" };
144
185
  const _hoisted_2$4 = ["src"];
145
186
  const _hoisted_3$2 = {
@@ -524,44 +565,147 @@
524
565
  });
525
566
  const index = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d8833363"]]);
526
567
  const FONT_SCALE_KEY = "hlw_font_scale";
527
- const FONT_SCALE_EVENT = "hlw:font-scale-change";
528
568
  const FONT_PRESETS = {
569
+ small: {
570
+ label: "小字体",
571
+ vars: {
572
+ "--font-xs": "16rpx",
573
+ "--font-sm": "20rpx",
574
+ "--font-base": "24rpx",
575
+ "--font-md": "28rpx",
576
+ "--font-lg": "32rpx",
577
+ "--font-xl": "36rpx"
578
+ }
579
+ },
529
580
  normal: {
530
581
  label: "标准",
531
- style: "--font-xs:20rpx;--font-sm:24rpx;--font-base:28rpx;--font-md:32rpx;--font-lg:36rpx;--font-xl:40rpx;"
582
+ vars: {
583
+ "--font-xs": "20rpx",
584
+ "--font-sm": "24rpx",
585
+ "--font-base": "28rpx",
586
+ "--font-md": "32rpx",
587
+ "--font-lg": "36rpx",
588
+ "--font-xl": "40rpx"
589
+ }
532
590
  },
533
591
  large: {
534
592
  label: "大字体",
535
- style: "--font-xs:24rpx;--font-sm:30rpx;--font-base:34rpx;--font-md:40rpx;--font-lg:46rpx;--font-xl:52rpx;"
593
+ vars: {
594
+ "--font-xs": "24rpx",
595
+ "--font-sm": "30rpx",
596
+ "--font-base": "34rpx",
597
+ "--font-md": "40rpx",
598
+ "--font-lg": "46rpx",
599
+ "--font-xl": "52rpx"
600
+ }
536
601
  },
537
602
  xlarge: {
538
603
  label: "超大字体",
539
- style: "--font-xs:28rpx;--font-sm:36rpx;--font-base:42rpx;--font-md:48rpx;--font-lg:56rpx;--font-xl:64rpx;"
604
+ vars: {
605
+ "--font-xs": "28rpx",
606
+ "--font-sm": "36rpx",
607
+ "--font-base": "42rpx",
608
+ "--font-md": "48rpx",
609
+ "--font-lg": "56rpx",
610
+ "--font-xl": "64rpx"
611
+ }
540
612
  }
541
613
  };
542
614
  function getCurrentFontScale() {
543
615
  try {
544
616
  const v = uni.getStorageSync(FONT_SCALE_KEY);
545
- if (v === "large" || v === "xlarge")
617
+ if (v === "small" || v === "large" || v === "xlarge")
546
618
  return v;
547
619
  } catch {
548
620
  }
549
621
  return "normal";
550
622
  }
551
- function getCurrentFontStyle() {
552
- return FONT_PRESETS[getCurrentFontScale()].style;
623
+ function getCurrentFontVars() {
624
+ return FONT_PRESETS[getCurrentFontScale()].vars;
553
625
  }
554
- function useFontPageStyle() {
555
- const fontPageStyle = vue.ref(getCurrentFontStyle());
556
- const onFontChange = () => {
557
- fontPageStyle.value = getCurrentFontStyle();
626
+ const { hexToRgba, darkenHex } = mpCore.useColor();
627
+ const THEME_COLOR_KEY = "hlw_theme_color";
628
+ const DEFAULT_THEMES = [
629
+ { label: "默认蓝", value: "#3b82f6" },
630
+ { label: "活力橙", value: "#f97316" },
631
+ { label: "翡翠绿", value: "#10b981" },
632
+ { label: "玫瑰红", value: "#f43f5e" },
633
+ { label: "紫罗兰", value: "#8b5cf6" },
634
+ { label: "青石灰", value: "#64748b" }
635
+ ];
636
+ function getCurrentThemeColor() {
637
+ try {
638
+ const v = uni.getStorageSync(THEME_COLOR_KEY);
639
+ if (v && typeof v === "string")
640
+ return v;
641
+ } catch {
642
+ }
643
+ return DEFAULT_THEMES[0].value;
644
+ }
645
+ function getCurrentThemeVars() {
646
+ const color = getCurrentThemeColor();
647
+ return {
648
+ "--primary-color": color,
649
+ "--primary-light": hexToRgba(color, 0.12),
650
+ "--primary-dark": darkenHex(color)
651
+ };
652
+ }
653
+ const { varsToStyle } = mpCore.useColor();
654
+ const THEME_CHANGE_EVENT = "hlw:theme-change";
655
+ function buildThemeStyle() {
656
+ return varsToStyle({
657
+ ...getCurrentFontVars(),
658
+ ...getCurrentThemeVars()
659
+ });
660
+ }
661
+ function useThemePageStyle() {
662
+ const themePageStyle = vue.ref(buildThemeStyle());
663
+ const onThemeChange = () => {
664
+ themePageStyle.value = buildThemeStyle();
558
665
  };
559
- vue.onMounted(() => uni.$on(FONT_SCALE_EVENT, onFontChange));
560
- vue.onUnmounted(() => uni.$off(FONT_SCALE_EVENT, onFontChange));
561
- return { fontPageStyle };
666
+ vue.onMounted(() => uni.$on(THEME_CHANGE_EVENT, onThemeChange));
667
+ vue.onUnmounted(() => uni.$off(THEME_CHANGE_EVENT, onThemeChange));
668
+ return { themePageStyle };
562
669
  }
670
+ const useThemeStore = pinia.defineStore(
671
+ "theme",
672
+ () => {
673
+ const scale = vue.ref("normal");
674
+ function setScale(s) {
675
+ scale.value = s;
676
+ uni.setStorageSync(FONT_SCALE_KEY, s);
677
+ uni.$emit(THEME_CHANGE_EVENT);
678
+ }
679
+ const fontOptions = Object.keys(FONT_PRESETS).map((key) => ({
680
+ value: key,
681
+ label: FONT_PRESETS[key].label
682
+ }));
683
+ const primaryColor = vue.ref(DEFAULT_THEMES[0].value);
684
+ const themes = DEFAULT_THEMES;
685
+ const activeTheme = vue.computed(
686
+ () => themes.find((t) => t.value === primaryColor.value) ?? { label: "自定义", value: primaryColor.value }
687
+ );
688
+ function setTheme(color) {
689
+ primaryColor.value = color;
690
+ uni.setStorageSync(THEME_COLOR_KEY, color);
691
+ uni.$emit(THEME_CHANGE_EVENT);
692
+ }
693
+ return {
694
+ // 字体
695
+ scale,
696
+ fontOptions,
697
+ setScale,
698
+ // 主题色
699
+ primaryColor,
700
+ themes,
701
+ activeTheme,
702
+ setTheme
703
+ };
704
+ },
705
+ { unistorage: true }
706
+ );
707
+ exports2.DEFAULT_THEMES = DEFAULT_THEMES;
563
708
  exports2.FONT_PRESETS = FONT_PRESETS;
564
- exports2.FONT_SCALE_EVENT = FONT_SCALE_EVENT;
565
709
  exports2.FONT_SCALE_KEY = FONT_SCALE_KEY;
566
710
  exports2.HlwAd = _sfc_main$7;
567
711
  exports2.HlwAvatar = index$6;
@@ -571,8 +715,14 @@
571
715
  exports2.HlwLoading = index$2;
572
716
  exports2.HlwMenu = index$1;
573
717
  exports2.HlwPage = index;
718
+ exports2.THEME_CHANGE_EVENT = THEME_CHANGE_EVENT;
719
+ exports2.THEME_COLOR_KEY = THEME_COLOR_KEY;
720
+ exports2.buildThemeStyle = buildThemeStyle;
574
721
  exports2.getCurrentFontScale = getCurrentFontScale;
575
- exports2.getCurrentFontStyle = getCurrentFontStyle;
576
- exports2.useFontPageStyle = useFontPageStyle;
722
+ exports2.getCurrentFontVars = getCurrentFontVars;
723
+ exports2.getCurrentThemeColor = getCurrentThemeColor;
724
+ exports2.getCurrentThemeVars = getCurrentThemeVars;
725
+ exports2.useThemePageStyle = useThemePageStyle;
726
+ exports2.useThemeStore = useThemeStore;
577
727
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
578
728
  });
package/dist/index.mjs CHANGED
@@ -1,4 +1,6 @@
1
- import { defineComponent, resolveComponent, openBlock, createBlock, ref, computed, createElementBlock, normalizeClass, createElementVNode, toDisplayString, renderSlot, createCommentVNode, useSlots, normalizeStyle, unref, Fragment, renderList, withCtx, createVNode, onMounted, onUnmounted } from "vue";
1
+ import { defineComponent, resolveComponent, openBlock, createBlock, ref, computed, createElementBlock, normalizeClass, createElementVNode, toDisplayString, useSlots, renderSlot, createCommentVNode, normalizeStyle, unref, Fragment, renderList, withCtx, createVNode, onMounted, onUnmounted } from "vue";
2
+ import { useColor } from "@hlw-uni/mp-core";
3
+ import { defineStore } from "pinia";
2
4
  const _sfc_main$7 = /* @__PURE__ */ defineComponent({
3
5
  ...{ name: "HlwAd" },
4
6
  __name: "index",
@@ -76,67 +78,108 @@ const _export_sfc = (sfc, props) => {
76
78
  return target;
77
79
  };
78
80
  const index$6 = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["__scopeId", "data-v-89dcbc96"]]);
79
- const _hoisted_1$5 = { class: "hlw-card" };
80
- const _hoisted_2$5 = {
81
+ const _hoisted_1$5 = {
81
82
  key: 0,
82
83
  class: "hlw-card-header"
83
84
  };
84
- const _hoisted_3$3 = { class: "flex items-center justify-between px-5 py-4 border-0 border-b border-dashed border-slate-200 bg-slate-50/30" };
85
- const _hoisted_4$2 = { key: 0 };
86
- const _hoisted_5$1 = { class: "text-sm font-bold text-slate-800 flex items-center gap-2 tracking-wide" };
87
- const _hoisted_6$1 = { key: 1 };
85
+ const _hoisted_2$5 = { class: "hlw-card-header-inner" };
86
+ const _hoisted_3$3 = { class: "hlw-card-header-left" };
87
+ const _hoisted_4$2 = {
88
+ key: 0,
89
+ class: "hlw-card-title"
90
+ };
91
+ const _hoisted_5$1 = {
92
+ key: 0,
93
+ class: "hlw-card-header-right"
94
+ };
95
+ const _hoisted_6$1 = {
96
+ key: 0,
97
+ class: "hlw-card-extra"
98
+ };
88
99
  const _hoisted_7$1 = {
100
+ key: 1,
101
+ class: "hlw-card-divider"
102
+ };
103
+ const _hoisted_8$1 = {
104
+ key: 2,
105
+ class: "hlw-card-footer"
106
+ };
107
+ const _hoisted_9$1 = { class: "hlw-card-footer-inner" };
108
+ const _hoisted_10$1 = { class: "hlw-card-footer-left" };
109
+ const _hoisted_11$1 = {
89
110
  key: 0,
90
- class: "text-[10px] text-slate-400 bg-slate-50 px-2 py-1 rounded border border-slate-100 tracking-wide"
111
+ class: "hlw-card-footer-right"
91
112
  };
92
- const _hoisted_8$1 = { class: "hlw-card-body" };
93
113
  const _sfc_main$5 = /* @__PURE__ */ defineComponent({
94
- ...{
95
- options: {
96
- styleIsolation: "shared",
97
- virtualHost: true
98
- }
99
- },
100
114
  __name: "index",
101
115
  props: {
102
116
  title: { default: "" },
103
- icon: { default: "" },
104
- iconColor: { default: "" },
105
- extra: { default: "" }
117
+ extra: { default: "" },
118
+ border: { type: Boolean, default: true },
119
+ radius: { default: "xl" },
120
+ divider: { type: Boolean, default: void 0 },
121
+ padding: { type: Boolean, default: true }
106
122
  },
107
123
  setup(__props) {
124
+ const props = __props;
125
+ const slots = useSlots();
126
+ const hasHeader = computed(
127
+ () => !!(props.title || props.extra || slots.header || slots["header-left"] || slots["header-right"])
128
+ );
129
+ const hasFooter = computed(
130
+ () => !!(slots.footer || slots["footer-left"] || slots["footer-right"])
131
+ );
132
+ const showDivider = computed(() => {
133
+ if (props.divider !== void 0)
134
+ return props.divider;
135
+ return hasHeader.value;
136
+ });
108
137
  return (_ctx, _cache) => {
109
- return openBlock(), createElementBlock("view", _hoisted_1$5, [
110
- _ctx.$slots.header || __props.title || __props.icon || _ctx.$slots["header-left"] || _ctx.$slots["header-right"] ? (openBlock(), createElementBlock("view", _hoisted_2$5, [
138
+ return openBlock(), createElementBlock("view", {
139
+ class: normalizeClass(["hlw-card", [
140
+ `hlw-card--radius-${__props.radius}`,
141
+ __props.border ? "hlw-card--bordered" : ""
142
+ ]])
143
+ }, [
144
+ hasHeader.value ? (openBlock(), createElementBlock("view", _hoisted_1$5, [
111
145
  renderSlot(_ctx.$slots, "header", {}, () => [
112
- createElementVNode("view", _hoisted_3$3, [
113
- _ctx.$slots["header-left"] || __props.title || __props.icon ? (openBlock(), createElementBlock("view", _hoisted_4$2, [
146
+ createElementVNode("view", _hoisted_2$5, [
147
+ createElementVNode("view", _hoisted_3$3, [
114
148
  renderSlot(_ctx.$slots, "header-left", {}, () => [
115
- createElementVNode("view", _hoisted_5$1, [
116
- __props.icon ? (openBlock(), createElementBlock("text", {
117
- key: 0,
118
- class: normalizeClass([__props.icon, __props.iconColor])
119
- }, null, 2)) : createCommentVNode("", true),
120
- createElementVNode("text", null, toDisplayString(__props.title), 1)
121
- ])
149
+ __props.title ? (openBlock(), createElementBlock("text", _hoisted_4$2, toDisplayString(__props.title), 1)) : createCommentVNode("", true)
122
150
  ], true)
123
- ])) : createCommentVNode("", true),
124
- _ctx.$slots["header-right"] || __props.extra ? (openBlock(), createElementBlock("view", _hoisted_6$1, [
151
+ ]),
152
+ _ctx.$slots["header-right"] || __props.extra ? (openBlock(), createElementBlock("view", _hoisted_5$1, [
125
153
  renderSlot(_ctx.$slots, "header-right", {}, () => [
126
- __props.extra ? (openBlock(), createElementBlock("text", _hoisted_7$1, toDisplayString(__props.extra), 1)) : createCommentVNode("", true)
154
+ __props.extra ? (openBlock(), createElementBlock("text", _hoisted_6$1, toDisplayString(__props.extra), 1)) : createCommentVNode("", true)
127
155
  ], true)
128
156
  ])) : createCommentVNode("", true)
129
157
  ])
130
158
  ], true)
131
159
  ])) : createCommentVNode("", true),
132
- createElementVNode("view", _hoisted_8$1, [
160
+ showDivider.value ? (openBlock(), createElementBlock("view", _hoisted_7$1)) : createCommentVNode("", true),
161
+ createElementVNode("view", {
162
+ class: normalizeClass(["hlw-card-body", { "hlw-card-body--padded": __props.padding }])
163
+ }, [
133
164
  renderSlot(_ctx.$slots, "default", {}, void 0, true)
134
- ])
135
- ]);
165
+ ], 2),
166
+ hasFooter.value ? (openBlock(), createElementBlock("view", _hoisted_8$1, [
167
+ renderSlot(_ctx.$slots, "footer", {}, () => [
168
+ createElementVNode("view", _hoisted_9$1, [
169
+ createElementVNode("view", _hoisted_10$1, [
170
+ renderSlot(_ctx.$slots, "footer-left", {}, void 0, true)
171
+ ]),
172
+ _ctx.$slots["footer-right"] ? (openBlock(), createElementBlock("view", _hoisted_11$1, [
173
+ renderSlot(_ctx.$slots, "footer-right", {}, void 0, true)
174
+ ])) : createCommentVNode("", true)
175
+ ])
176
+ ], true)
177
+ ])) : createCommentVNode("", true)
178
+ ], 2);
136
179
  };
137
180
  }
138
181
  });
139
- const index$5 = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-787fc3a7"]]);
182
+ const index$5 = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-cf55252e"]]);
140
183
  const _hoisted_1$4 = { class: "hlw-empty" };
141
184
  const _hoisted_2$4 = ["src"];
142
185
  const _hoisted_3$2 = {
@@ -521,45 +564,148 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
521
564
  });
522
565
  const index = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d8833363"]]);
523
566
  const FONT_SCALE_KEY = "hlw_font_scale";
524
- const FONT_SCALE_EVENT = "hlw:font-scale-change";
525
567
  const FONT_PRESETS = {
568
+ small: {
569
+ label: "小字体",
570
+ vars: {
571
+ "--font-xs": "16rpx",
572
+ "--font-sm": "20rpx",
573
+ "--font-base": "24rpx",
574
+ "--font-md": "28rpx",
575
+ "--font-lg": "32rpx",
576
+ "--font-xl": "36rpx"
577
+ }
578
+ },
526
579
  normal: {
527
580
  label: "标准",
528
- style: "--font-xs:20rpx;--font-sm:24rpx;--font-base:28rpx;--font-md:32rpx;--font-lg:36rpx;--font-xl:40rpx;"
581
+ vars: {
582
+ "--font-xs": "20rpx",
583
+ "--font-sm": "24rpx",
584
+ "--font-base": "28rpx",
585
+ "--font-md": "32rpx",
586
+ "--font-lg": "36rpx",
587
+ "--font-xl": "40rpx"
588
+ }
529
589
  },
530
590
  large: {
531
591
  label: "大字体",
532
- style: "--font-xs:24rpx;--font-sm:30rpx;--font-base:34rpx;--font-md:40rpx;--font-lg:46rpx;--font-xl:52rpx;"
592
+ vars: {
593
+ "--font-xs": "24rpx",
594
+ "--font-sm": "30rpx",
595
+ "--font-base": "34rpx",
596
+ "--font-md": "40rpx",
597
+ "--font-lg": "46rpx",
598
+ "--font-xl": "52rpx"
599
+ }
533
600
  },
534
601
  xlarge: {
535
602
  label: "超大字体",
536
- style: "--font-xs:28rpx;--font-sm:36rpx;--font-base:42rpx;--font-md:48rpx;--font-lg:56rpx;--font-xl:64rpx;"
603
+ vars: {
604
+ "--font-xs": "28rpx",
605
+ "--font-sm": "36rpx",
606
+ "--font-base": "42rpx",
607
+ "--font-md": "48rpx",
608
+ "--font-lg": "56rpx",
609
+ "--font-xl": "64rpx"
610
+ }
537
611
  }
538
612
  };
539
613
  function getCurrentFontScale() {
540
614
  try {
541
615
  const v = uni.getStorageSync(FONT_SCALE_KEY);
542
- if (v === "large" || v === "xlarge")
616
+ if (v === "small" || v === "large" || v === "xlarge")
543
617
  return v;
544
618
  } catch {
545
619
  }
546
620
  return "normal";
547
621
  }
548
- function getCurrentFontStyle() {
549
- return FONT_PRESETS[getCurrentFontScale()].style;
622
+ function getCurrentFontVars() {
623
+ return FONT_PRESETS[getCurrentFontScale()].vars;
550
624
  }
551
- function useFontPageStyle() {
552
- const fontPageStyle = ref(getCurrentFontStyle());
553
- const onFontChange = () => {
554
- fontPageStyle.value = getCurrentFontStyle();
625
+ const { hexToRgba, darkenHex } = useColor();
626
+ const THEME_COLOR_KEY = "hlw_theme_color";
627
+ const DEFAULT_THEMES = [
628
+ { label: "默认蓝", value: "#3b82f6" },
629
+ { label: "活力橙", value: "#f97316" },
630
+ { label: "翡翠绿", value: "#10b981" },
631
+ { label: "玫瑰红", value: "#f43f5e" },
632
+ { label: "紫罗兰", value: "#8b5cf6" },
633
+ { label: "青石灰", value: "#64748b" }
634
+ ];
635
+ function getCurrentThemeColor() {
636
+ try {
637
+ const v = uni.getStorageSync(THEME_COLOR_KEY);
638
+ if (v && typeof v === "string")
639
+ return v;
640
+ } catch {
641
+ }
642
+ return DEFAULT_THEMES[0].value;
643
+ }
644
+ function getCurrentThemeVars() {
645
+ const color = getCurrentThemeColor();
646
+ return {
647
+ "--primary-color": color,
648
+ "--primary-light": hexToRgba(color, 0.12),
649
+ "--primary-dark": darkenHex(color)
650
+ };
651
+ }
652
+ const { varsToStyle } = useColor();
653
+ const THEME_CHANGE_EVENT = "hlw:theme-change";
654
+ function buildThemeStyle() {
655
+ return varsToStyle({
656
+ ...getCurrentFontVars(),
657
+ ...getCurrentThemeVars()
658
+ });
659
+ }
660
+ function useThemePageStyle() {
661
+ const themePageStyle = ref(buildThemeStyle());
662
+ const onThemeChange = () => {
663
+ themePageStyle.value = buildThemeStyle();
555
664
  };
556
- onMounted(() => uni.$on(FONT_SCALE_EVENT, onFontChange));
557
- onUnmounted(() => uni.$off(FONT_SCALE_EVENT, onFontChange));
558
- return { fontPageStyle };
665
+ onMounted(() => uni.$on(THEME_CHANGE_EVENT, onThemeChange));
666
+ onUnmounted(() => uni.$off(THEME_CHANGE_EVENT, onThemeChange));
667
+ return { themePageStyle };
559
668
  }
669
+ const useThemeStore = defineStore(
670
+ "theme",
671
+ () => {
672
+ const scale = ref("normal");
673
+ function setScale(s) {
674
+ scale.value = s;
675
+ uni.setStorageSync(FONT_SCALE_KEY, s);
676
+ uni.$emit(THEME_CHANGE_EVENT);
677
+ }
678
+ const fontOptions = Object.keys(FONT_PRESETS).map((key) => ({
679
+ value: key,
680
+ label: FONT_PRESETS[key].label
681
+ }));
682
+ const primaryColor = ref(DEFAULT_THEMES[0].value);
683
+ const themes = DEFAULT_THEMES;
684
+ const activeTheme = computed(
685
+ () => themes.find((t) => t.value === primaryColor.value) ?? { label: "自定义", value: primaryColor.value }
686
+ );
687
+ function setTheme(color) {
688
+ primaryColor.value = color;
689
+ uni.setStorageSync(THEME_COLOR_KEY, color);
690
+ uni.$emit(THEME_CHANGE_EVENT);
691
+ }
692
+ return {
693
+ // 字体
694
+ scale,
695
+ fontOptions,
696
+ setScale,
697
+ // 主题色
698
+ primaryColor,
699
+ themes,
700
+ activeTheme,
701
+ setTheme
702
+ };
703
+ },
704
+ { unistorage: true }
705
+ );
560
706
  export {
707
+ DEFAULT_THEMES,
561
708
  FONT_PRESETS,
562
- FONT_SCALE_EVENT,
563
709
  FONT_SCALE_KEY,
564
710
  _sfc_main$7 as HlwAd,
565
711
  index$6 as HlwAvatar,
@@ -569,7 +715,13 @@ export {
569
715
  index$2 as HlwLoading,
570
716
  index$1 as HlwMenu,
571
717
  index as HlwPage,
718
+ THEME_CHANGE_EVENT,
719
+ THEME_COLOR_KEY,
720
+ buildThemeStyle,
572
721
  getCurrentFontScale,
573
- getCurrentFontStyle,
574
- useFontPageStyle
722
+ getCurrentFontVars,
723
+ getCurrentThemeColor,
724
+ getCurrentThemeVars,
725
+ useThemePageStyle,
726
+ useThemeStore
575
727
  };
@@ -0,0 +1 @@
1
+ export declare const useThemeStore: any;
package/dist/style.css CHANGED
@@ -33,10 +33,95 @@
33
33
  .hlw-avatar--large .hlw-avatar__initial[data-v-89dcbc96] { font-size: var(--font-xl, 40rpx);
34
34
  }
35
35
 
36
- .hlw-card[data-v-787fc3a7] {
37
- @apply bg-white rounded-xl border border-solid border-slate-200 overflow-hidden w-full;
36
+ .hlw-card[data-v-cf55252e] {
37
+ width: 100%;
38
+ background: #fff;
39
+ overflow: hidden;
40
+ /* 圆角档位 */
41
+ }
42
+ .hlw-card--radius-none[data-v-cf55252e] {
43
+ border-radius: 0;
44
+ }
45
+ .hlw-card--radius-sm[data-v-cf55252e] {
46
+ border-radius: var(--radius-sm, 8rpx);
47
+ }
48
+ .hlw-card--radius-md[data-v-cf55252e] {
49
+ border-radius: var(--radius-md, 16rpx);
50
+ }
51
+ .hlw-card--radius-lg[data-v-cf55252e] {
52
+ border-radius: var(--radius-lg, 24rpx);
53
+ }
54
+ .hlw-card--radius-xl[data-v-cf55252e] {
55
+ border-radius: var(--radius-xl, 32rpx);
56
+ }
57
+ .hlw-card[data-v-cf55252e] {
58
+ /* 边框 */
59
+ }
60
+ .hlw-card--bordered[data-v-cf55252e] {
61
+ border: 1rpx solid var(--border-color, #e2e8f0);
62
+ }
63
+
64
+ /* 头部 */
65
+ .hlw-card-header[data-v-cf55252e] {
66
+ width: 100%;
67
+ }
68
+ .hlw-card-header-inner[data-v-cf55252e] {
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: space-between;
72
+ padding: 24rpx 28rpx;
73
+ }
74
+ .hlw-card-header-left[data-v-cf55252e] {
75
+ flex: 1;
76
+ min-width: 0;
77
+ }
78
+ .hlw-card-header-right[data-v-cf55252e] {
79
+ flex-shrink: 0;
80
+ margin-left: 16rpx;
81
+ }
82
+ .hlw-card-title[data-v-cf55252e] {
83
+ font-size: var(--font-sm, 24rpx);
84
+ font-weight: 700;
85
+ color: #1e293b;
86
+ letter-spacing: 0.02em;
87
+ }
88
+ .hlw-card-extra[data-v-cf55252e] {
89
+ font-size: var(--font-xs, 20rpx);
90
+ color: #94a3b8;
38
91
  }
39
92
 
93
+ /* 虚线分隔 */
94
+ .hlw-card-divider[data-v-cf55252e] {
95
+ width: 100%;
96
+ border-bottom: 1rpx dashed var(--border-color, #e2e8f0);
97
+ }
98
+
99
+ /* 内容区 */
100
+ .hlw-card-body[data-v-cf55252e] {
101
+ width: 100%;
102
+ }
103
+ .hlw-card-body--padded[data-v-cf55252e] {
104
+ padding: 24rpx 28rpx;
105
+ }
106
+
107
+ /* 底部 */
108
+ .hlw-card-footer[data-v-cf55252e] {
109
+ width: 100%;
110
+ border-top: 1rpx solid var(--border-color-light, #f1f5f9);
111
+ }
112
+ .hlw-card-footer-inner[data-v-cf55252e] {
113
+ display: flex;
114
+ align-items: center;
115
+ padding: 20rpx 28rpx;
116
+ }
117
+ .hlw-card-footer-left[data-v-cf55252e] {
118
+ flex: 1;
119
+ min-width: 0;
120
+ }
121
+ .hlw-card-footer-right[data-v-cf55252e] {
122
+ flex-shrink: 0;
123
+ margin-left: 16rpx;
124
+ }
40
125
  .hlw-empty[data-v-08b8d8fe] {
41
126
  display: flex;
42
127
  flex-direction: column;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hlw-uni/mp-vue",
3
- "version": "1.0.33",
3
+ "version": "1.0.35",
4
4
  "description": "hlw-uni Vue 组件库 — 供小程序业务方使用的 UI 组件集合",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
@@ -26,19 +26,19 @@
26
26
  "access": "public"
27
27
  },
28
28
  "peerDependencies": {
29
- "vue": ">=3.4.0",
30
- "pinia": ">=2.0.0"
29
+ "pinia": ">=2.0.0",
30
+ "vue": ">=3.4.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@dcloudio/types": "^3.4.8",
34
- "@types/node": "^20.11.0",
35
- "@vitejs/plugin-vue": "^5.0.0",
36
- "typescript": "^4.9.4",
33
+ "@dcloudio/types": "^3.4.30",
34
+ "@types/node": "^20.19.39",
35
+ "@vitejs/plugin-vue": "^5.2.4",
36
+ "typescript": "^4.9.5",
37
37
  "vite": "5.2.8",
38
38
  "vite-plugin-dts": "^4.5.4",
39
- "vue": "^3.4.0"
39
+ "vue": "^3.5.32"
40
40
  },
41
41
  "dependencies": {
42
- "@hlw-uni/mp-core": "^1.0.10"
42
+ "@hlw-uni/mp-core": "^1.0.28"
43
43
  }
44
44
  }
@@ -0,0 +1,51 @@
1
+ export type FontScale = "small" | "normal" | "large" | "xlarge";
2
+
3
+ export interface FontPreset {
4
+ label: string;
5
+ vars: Record<string, string>;
6
+ }
7
+
8
+ export const FONT_SCALE_KEY = "hlw_font_scale";
9
+
10
+ export const FONT_PRESETS: Record<FontScale, FontPreset> = {
11
+ small: {
12
+ label: "小字体",
13
+ vars: {
14
+ "--font-xs": "16rpx", "--font-sm": "20rpx", "--font-base": "24rpx",
15
+ "--font-md": "28rpx", "--font-lg": "32rpx", "--font-xl": "36rpx",
16
+ },
17
+ },
18
+ normal: {
19
+ label: "标准",
20
+ vars: {
21
+ "--font-xs": "20rpx", "--font-sm": "24rpx", "--font-base": "28rpx",
22
+ "--font-md": "32rpx", "--font-lg": "36rpx", "--font-xl": "40rpx",
23
+ },
24
+ },
25
+ large: {
26
+ label: "大字体",
27
+ vars: {
28
+ "--font-xs": "24rpx", "--font-sm": "30rpx", "--font-base": "34rpx",
29
+ "--font-md": "40rpx", "--font-lg": "46rpx", "--font-xl": "52rpx",
30
+ },
31
+ },
32
+ xlarge: {
33
+ label: "超大字体",
34
+ vars: {
35
+ "--font-xs": "28rpx", "--font-sm": "36rpx", "--font-base": "42rpx",
36
+ "--font-md": "48rpx", "--font-lg": "56rpx", "--font-xl": "64rpx",
37
+ },
38
+ },
39
+ };
40
+
41
+ export function getCurrentFontScale(): FontScale {
42
+ try {
43
+ const v = uni.getStorageSync(FONT_SCALE_KEY);
44
+ if (v === "small" || v === "large" || v === "xlarge") return v;
45
+ } catch {}
46
+ return "normal";
47
+ }
48
+
49
+ export function getCurrentFontVars(): Record<string, string> {
50
+ return FONT_PRESETS[getCurrentFontScale()].vars;
51
+ }
@@ -0,0 +1,33 @@
1
+ import { ref, onMounted, onUnmounted } from "vue";
2
+ import { useColor } from "@hlw-uni/mp-core";
3
+
4
+ const { varsToStyle } = useColor();
5
+ import { getCurrentFontVars } from "./font";
6
+ import { getCurrentThemeVars } from "./palette";
7
+
8
+ export const THEME_CHANGE_EVENT = "hlw:theme-change";
9
+
10
+ export function buildThemeStyle(): string {
11
+ return varsToStyle({
12
+ ...getCurrentFontVars(),
13
+ ...getCurrentThemeVars(),
14
+ });
15
+ }
16
+
17
+ export function useThemePageStyle() {
18
+ const themePageStyle = ref(buildThemeStyle());
19
+
20
+ const onThemeChange = () => {
21
+ themePageStyle.value = buildThemeStyle();
22
+ };
23
+
24
+ onMounted(() => uni.$on(THEME_CHANGE_EVENT, onThemeChange));
25
+ onUnmounted(() => uni.$off(THEME_CHANGE_EVENT, onThemeChange));
26
+
27
+ return { themePageStyle };
28
+ }
29
+
30
+ export type { FontScale, FontPreset } from "./font";
31
+ export { FONT_SCALE_KEY, FONT_PRESETS, getCurrentFontScale, getCurrentFontVars } from "./font";
32
+ export type { ThemeColor } from "./palette";
33
+ export { THEME_COLOR_KEY, DEFAULT_THEMES, getCurrentThemeColor, getCurrentThemeVars } from "./palette";
@@ -0,0 +1,36 @@
1
+ import { useColor } from "@hlw-uni/mp-core";
2
+
3
+ const { hexToRgba, darkenHex } = useColor();
4
+
5
+ export interface ThemeColor {
6
+ label: string;
7
+ value: string;
8
+ }
9
+
10
+ export const THEME_COLOR_KEY = "hlw_theme_color";
11
+
12
+ export const DEFAULT_THEMES: ThemeColor[] = [
13
+ { label: "默认蓝", value: "#3b82f6" },
14
+ { label: "活力橙", value: "#f97316" },
15
+ { label: "翡翠绿", value: "#10b981" },
16
+ { label: "玫瑰红", value: "#f43f5e" },
17
+ { label: "紫罗兰", value: "#8b5cf6" },
18
+ { label: "青石灰", value: "#64748b" },
19
+ ];
20
+
21
+ export function getCurrentThemeColor(): string {
22
+ try {
23
+ const v = uni.getStorageSync(THEME_COLOR_KEY);
24
+ if (v && typeof v === "string") return v;
25
+ } catch {}
26
+ return DEFAULT_THEMES[0].value;
27
+ }
28
+
29
+ export function getCurrentThemeVars(): Record<string, string> {
30
+ const color = getCurrentThemeColor();
31
+ return {
32
+ "--primary-color": color,
33
+ "--primary-light": hexToRgba(color, 0.12),
34
+ "--primary-dark": darkenHex(color),
35
+ };
36
+ }
package/src/index.ts CHANGED
@@ -11,6 +11,6 @@ export { default as HlwLoading } from "./components/hlw-loading/index.vue";
11
11
  export { default as HlwMenu } from "./components/hlw-menu/index.vue";
12
12
  export type { HlwMenuItem } from "./components/hlw-menu/types";
13
13
  export { default as HlwPage } from "./components/hlw-page/index.vue";
14
- export type { FontScale, FontPreset, ThemeColor } from "./composables/theme-presets";
15
- export { FONT_PRESETS, FONT_SCALE_KEY, DEFAULT_THEMES, THEME_COLOR_KEY, THEME_CHANGE_EVENT, getCurrentFontScale, getCurrentFontStyle, getCurrentThemeColor, getCurrentThemeStyle, useThemePageStyle } from "./composables/theme-presets";
14
+ export type { FontScale, FontPreset, ThemeColor } from "./composables/theme";
15
+ export { FONT_PRESETS, FONT_SCALE_KEY, DEFAULT_THEMES, THEME_COLOR_KEY, THEME_CHANGE_EVENT, getCurrentFontScale, getCurrentFontVars, getCurrentThemeColor, getCurrentThemeVars, buildThemeStyle, useThemePageStyle } from "./composables/theme";
16
16
  export { useThemeStore } from "./stores/theme";
@@ -10,8 +10,8 @@ import {
10
10
  DEFAULT_THEMES,
11
11
  THEME_COLOR_KEY,
12
12
  THEME_CHANGE_EVENT,
13
- } from "../composables/theme-presets";
14
- import type { FontScale, ThemeColor } from "../composables/theme-presets";
13
+ } from "../composables/theme";
14
+ import type { FontScale, ThemeColor } from "../composables/theme";
15
15
 
16
16
  export const useThemeStore = defineStore(
17
17
  "theme",
@@ -1,54 +0,0 @@
1
- /**
2
- * hlw-page 字体档位预设
3
- *
4
- * 三档:标准 / 大字体 / 超大字体
5
- * 通过 <page-meta page-style="..."> 注入到页面根节点,
6
- * 覆盖 CSS 变量,所有使用 var(--font-*) 的组件自动跟随。
7
- */
8
- export type FontScale = "normal" | "large" | "xlarge";
9
- /** storage key,与 qz2 font store 保持一致 */
10
- export declare const FONT_SCALE_KEY = "hlw_font_scale";
11
- /** 全局事件名,store.setScale 触发后 hlw-page 实时响应 */
12
- export declare const FONT_SCALE_EVENT = "hlw:font-scale-change";
13
- export interface FontPreset {
14
- /** 展示名称 */
15
- label: string;
16
- /** 注入到 page-meta 的 CSS 变量字符串 */
17
- style: string;
18
- }
19
- /**
20
- * 三档字体预设
21
- *
22
- * 变量说明:
23
- * --font-xs 极小文字(角标、辅助标注)
24
- * --font-sm 小文字(次要说明、标签、grid-label)
25
- * --font-base 正文(菜单项、内容主体)
26
- * --font-md 中等(次级标题)
27
- * --font-lg 大号(页面标题、导航标题)
28
- * --font-xl 特大(数字展示)
29
- */
30
- export declare const FONT_PRESETS: Record<FontScale, FontPreset>;
31
- /** 读取当前档位(同步,从 storage 取) */
32
- export declare function getCurrentFontScale(): FontScale;
33
- /** 读取当前档位对应的 page-style 字符串 */
34
- export declare function getCurrentFontStyle(): string;
35
- /**
36
- * 在页面根节点使用,配合 <page-meta :page-style="fontPageStyle"> 实现全局字体缩放。
37
- *
38
- * 注意:<page-meta> 必须作为页面 .vue 文件 template 的第一个根节点,不可放在子组件内。
39
- *
40
- * @example
41
- * ```vue
42
- * <template>
43
- * <page-meta :page-style="fontPageStyle" />
44
- * <hlw-page title="xxx">...</hlw-page>
45
- * </template>
46
- * <script setup>
47
- * import { useFontPageStyle } from '@hlw-uni/mp-vue';
48
- * const { fontPageStyle } = useFontPageStyle();
49
- * </script>
50
- * ```
51
- */
52
- export declare function useFontPageStyle(): {
53
- fontPageStyle: any;
54
- };
@@ -1,109 +0,0 @@
1
- import { ref, computed, onMounted, onUnmounted } from "vue";
2
-
3
- // ─── 字体档位 ────────────────────────────────────────
4
-
5
- export type FontScale = "small" | "normal" | "large" | "xlarge";
6
-
7
- export const FONT_SCALE_KEY = "hlw_font_scale";
8
-
9
- export interface FontPreset {
10
- label: string;
11
- style: string;
12
- }
13
-
14
- export const FONT_PRESETS: Record<FontScale, FontPreset> = {
15
- small: {
16
- label: "小字体",
17
- style: "--font-xs:16rpx;--font-sm:20rpx;--font-base:24rpx;--font-md:28rpx;--font-lg:32rpx;--font-xl:36rpx;",
18
- },
19
- normal: {
20
- label: "标准",
21
- style: "--font-xs:20rpx;--font-sm:24rpx;--font-base:28rpx;--font-md:32rpx;--font-lg:36rpx;--font-xl:40rpx;",
22
- },
23
- large: {
24
- label: "大字体",
25
- style: "--font-xs:24rpx;--font-sm:30rpx;--font-base:34rpx;--font-md:40rpx;--font-lg:46rpx;--font-xl:52rpx;",
26
- },
27
- xlarge: {
28
- label: "超大字体",
29
- style: "--font-xs:28rpx;--font-sm:36rpx;--font-base:42rpx;--font-md:48rpx;--font-lg:56rpx;--font-xl:64rpx;",
30
- },
31
- };
32
-
33
- export function getCurrentFontScale(): FontScale {
34
- try {
35
- const v = uni.getStorageSync(FONT_SCALE_KEY);
36
- if (v === "small" || v === "large" || v === "xlarge") return v;
37
- } catch {}
38
- return "normal";
39
- }
40
-
41
- export function getCurrentFontStyle(): string {
42
- return FONT_PRESETS[getCurrentFontScale()].style;
43
- }
44
-
45
- // ─── 主题色 ──────────────────────────────────────────
46
-
47
- export interface ThemeColor {
48
- label: string;
49
- value: string;
50
- }
51
-
52
- export const THEME_COLOR_KEY = "hlw_theme_color";
53
-
54
- /** 内置预设主题色 */
55
- export const DEFAULT_THEMES: ThemeColor[] = [
56
- { label: "默认蓝", value: "#3b82f6" },
57
- { label: "活力橙", value: "#f97316" },
58
- { label: "翡翠绿", value: "#10b981" },
59
- { label: "玫瑰红", value: "#f43f5e" },
60
- { label: "紫罗兰", value: "#8b5cf6" },
61
- { label: "青石灰", value: "#64748b" },
62
- ];
63
-
64
- export function getCurrentThemeColor(): string {
65
- try {
66
- const v = uni.getStorageSync(THEME_COLOR_KEY);
67
- if (v && typeof v === "string") return v;
68
- } catch {}
69
- return DEFAULT_THEMES[0].value;
70
- }
71
-
72
- export function getCurrentThemeStyle(): string {
73
- return `--primary-color:${getCurrentThemeColor()};`;
74
- }
75
-
76
- // ─── 统一事件 ────────────────────────────────────────
77
-
78
- /** 字体 / 主题色变更时统一广播此事件 */
79
- export const THEME_CHANGE_EVENT = "hlw:theme-change";
80
-
81
- // ─── 组合式函数 ──────────────────────────────────────
82
-
83
- /**
84
- * 在页面中使用,配合 <page-meta :page-style="themePageStyle"> 注入字体 + 主题色。
85
- *
86
- * @example
87
- * ```vue
88
- * <template>
89
- * <page-meta :page-style="themePageStyle" />
90
- * <hlw-page title="xxx">...</hlw-page>
91
- * </template>
92
- * <script setup>
93
- * import { useThemePageStyle } from '@hlw-uni/mp-vue';
94
- * const { themePageStyle } = useThemePageStyle();
95
- * </script>
96
- * ```
97
- */
98
- export function useThemePageStyle() {
99
- const themePageStyle = ref(getCurrentFontStyle() + getCurrentThemeStyle());
100
-
101
- const onThemeChange = () => {
102
- themePageStyle.value = getCurrentFontStyle() + getCurrentThemeStyle();
103
- };
104
-
105
- onMounted(() => uni.$on(THEME_CHANGE_EVENT, onThemeChange));
106
- onUnmounted(() => uni.$off(THEME_CHANGE_EVENT, onThemeChange));
107
-
108
- return { themePageStyle };
109
- }