@hlw-uni/mp-vue 2.0.0 → 2.0.4

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.
package/dist/app.d.ts CHANGED
@@ -10,7 +10,7 @@ export interface InterceptorOptions {
10
10
  /** Token 来源函数 */
11
11
  getToken?: () => string;
12
12
  /** 登录失效时的处理函数 */
13
- onUnauthorized?: () => void;
13
+ onLogout?: () => void;
14
14
  /** 接口业务错误码是否自动 toast */
15
15
  autoToastError?: boolean;
16
16
  }
@@ -28,6 +28,6 @@ export declare function useApp(): {
28
28
  /**
29
29
  * 注册默认请求、响应和错误拦截器。
30
30
  */
31
- export declare function setupDefaultInterceptors(options?: InterceptorOptions & {
31
+ export declare function setupInterceptors(options?: InterceptorOptions & {
32
32
  sigSecret?: string;
33
33
  }): void;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Adapter 返回值解包:兼容业务方传「已解包 T」或「ThinkAdmin envelope { code, data }」。
3
+ *
4
+ * 业务方写法:
5
+ * setConfigAd({ getConfig: getAdConfig }) // 直接传 envelope-returning 函数
6
+ * setConfigAd({ getConfig: async () => myUnwrapped }) // 也支持已解包
7
+ *
8
+ * 鸭子类型识别:raw 是对象且有 number 类型的 code 字段 → 当 envelope 处理。
9
+ */
10
+ export type AdapterPayload<T> = T | null | {
11
+ code: number;
12
+ data?: T;
13
+ info?: string;
14
+ };
15
+ export declare function unwrapPayload<T>(raw: AdapterPayload<T>): T | null;
@@ -1,47 +1,6 @@
1
+ import { AdapterPayload } from '../_internal/unwrap';
1
2
  import { Ref } from 'vue';
2
- /**
3
- * useAd —— 完整的广告业务封装
4
- *
5
- * 内置了:
6
- * - 6 个 unit_id 配置(store 缓存,调 getConfig 拉一次)
7
- * - 激励视频:广告加载 loading → onShown 自动关闭 → 中途关闭挽留 modal → 看完调业务方的 claim 发奖
8
- * - 插屏:一行调用
9
- * - 底层 SDK 适配(onLoad / offClose / show 兜底 / 实例缓存)
10
- *
11
- * 业务侧使用方式:
12
- *
13
- * 1. 在 App.vue 注入「全局」回调(拿配置 + 校验登录):
14
- *
15
- * import { configAd } from "@hlw-uni/mp-vue";
16
- * import { getAdConfig } from "@/api/ad";
17
- * import { useUserStore } from "@/store";
18
- *
19
- * configAd({
20
- * getConfig: async () => {
21
- * const res = await getAdConfig();
22
- * return res.code === 1 && res.data ? res.data : null;
23
- * },
24
- * isAuth: () => !!useUserStore().token,
25
- * });
26
- *
27
- * 2. 任意页面拉配置 / 用 unit_id 渲染:
28
- *
29
- * const { config, loadConfig, showPopup } = useAd();
30
- * await loadConfig();
31
- * <hlw-ad type="banner" :unit-id="config.banner" />
32
- *
33
- * 3. 看广告领奖励(业务方按场景传不同的 claim 接口):
34
- *
35
- * const ok = await showReward(async () => {
36
- * const res = await claimAdReward();
37
- * return res.code === 1
38
- * ? { ok: true, reward: res.data?.reward }
39
- * : { ok: false, msg: res.info };
40
- * });
41
- *
42
- * // 不传 claim 就是纯展示,看完即返回 true
43
- * const ok = await showReward();
44
- */
3
+
45
4
  /** 6 种广告类型 */
46
5
  export type AdType = "banner" | "grid" | "custom" | "video" | "reward" | "popup";
47
6
  /** 广告配置 —— 字段名跟后端表列名对齐(plugin_qz_mp.{type}_unit_id) */
@@ -58,10 +17,18 @@ export interface AdError {
58
17
  errCode: number;
59
18
  errMsg: string;
60
19
  }
61
- /** 业务回调注入接口 —— configAd 时由项目提供 */
20
+ /**
21
+ * 业务回调注入接口 —— setConfigAd 时由项目提供。
22
+ *
23
+ * getConfig 支持两种返回:
24
+ * - 已解包:直接返回 AdConfig 或 null
25
+ * - ThinkAdmin envelope:返回 { code, data } 对象,库自动按 code===1 解包
26
+ *
27
+ * 业务方可以直接传 envelope-returning 的接口函数引用:
28
+ * setConfigAd({ getConfig: getAdConfig })
29
+ */
62
30
  export interface AdAdapter {
63
- /** 拉取广告配置;返回 null 表示拉失败,store 不更新 */
64
- getConfig: () => Promise<AdConfig | null>;
31
+ getConfig: () => Promise<AdapterPayload<AdConfig>>;
65
32
  /** 是否已登录;不传 = 不校验(showReward 调用前会问一次) */
66
33
  isAuth?: () => boolean;
67
34
  }
@@ -92,7 +59,7 @@ export type AdClaimFn = (closeRes: AdCloseResult) => Promise<AdClaimResult>;
92
59
  * 注入业务回调,应用启动时调用一次。
93
60
  * 不调用也不会崩,但 loadConfig / showReward 会无效。
94
61
  */
95
- export declare function configAd(a: AdAdapter): void;
62
+ export declare function setConfigAd(a: AdAdapter): void;
96
63
  export declare function useAd(): {
97
64
  config: Ref<{
98
65
  banner_unit_id: string;
@@ -1,4 +1,6 @@
1
+ import { AdapterPayload } from '../_internal/unwrap';
1
2
  import { ComputedRef } from 'vue';
3
+
2
4
  /** 后端返回的客服配置(按 button open-type=contact 标准属性命名) */
3
5
  export interface ContactConfig {
4
6
  send_message_title: string;
@@ -6,10 +8,12 @@ export interface ContactConfig {
6
8
  send_message_img: string;
7
9
  show_message_card: boolean;
8
10
  }
9
- /** Adapter 注入接口 */
11
+ /**
12
+ * Adapter 注入接口 —— getConfig 支持「已解包」或「ThinkAdmin envelope」两种返回。
13
+ * 业务方可以直接传 envelope-returning 接口:setConfigContact({ getConfig: getContactConfig })
14
+ */
10
15
  export interface ContactAdapter {
11
- /** 拉取客服配置;返回 null 表示拉失败,store 不更新 */
12
- getConfig: () => Promise<ContactConfig | null>;
16
+ getConfig: () => Promise<AdapterPayload<ContactConfig>>;
13
17
  }
14
18
  /** v-bind 到 button 的 camelCase props(微信原生属性约定) */
15
19
  export interface ContactBindProps {
@@ -21,7 +25,7 @@ export interface ContactBindProps {
21
25
  /**
22
26
  * 注入业务回调(应用启动时调用一次;不调用则 useContact 始终返回空字段)。
23
27
  */
24
- export declare function configContact(a: ContactAdapter): void;
28
+ export declare function setConfigContact(a: ContactAdapter): void;
25
29
  /**
26
30
  * 返回 v-bind 友好的客服配置 computed(首次调用会异步拉一次配置)。
27
31
  */
@@ -10,9 +10,9 @@ export { usePageMeta } from './page-meta';
10
10
  export { useStorage, type StorageInstance } from './storage';
11
11
  export { useValidate } from './validate';
12
12
  export { useFormat } from './format';
13
- export { useAd, configAd, destroyAds, confirmReward, type AdType, type AdConfig, type AdError, type AdAdapter, type AdCloseResult, type AdClaimResult, type AdClaimFn, } from './ad';
14
- export { useShare, useShareConfig, configShare, type ShareConfig, type ShareConfigResolver, type ShareFrom, type ShareAppMessageContent, type ShareTimelineContent, type ShareConfigMap, type ShareConfigAdapter, type PageShareItem, type PageShareFallback, type SharePayload, } from './share';
15
- export { useContact, configContact, type ContactConfig, type ContactAdapter, type ContactBindProps, } from './contact';
13
+ export { useAd, setConfigAd, destroyAds, confirmReward, type AdType, type AdConfig, type AdError, type AdAdapter, type AdCloseResult, type AdClaimResult, type AdClaimFn, } from './ad';
14
+ export { useShare, useShareConfig, setConfigShare, type ShareConfig, type ShareConfigResolver, type ShareFrom, type ShareAppMessageContent, type ShareTimelineContent, type ShareConfigMap, type ShareConfigAdapter, type PageShareItem, type PageShareFallback, type SharePayload, } from './share';
15
+ export { useContact, setConfigContact, type ContactConfig, type ContactAdapter, type ContactBindProps, } from './contact';
16
16
  export { useUtils, type DownloadFileOptions, type DownloadFileResult, type TapEvent } from './utils';
17
17
  export { useColor } from './color';
18
18
  export { useRouter, type NavigateType, type NavigateOptions } from './navigator';
@@ -1,3 +1,5 @@
1
+ import { AdapterPayload } from '../_internal/unwrap';
2
+
1
3
  export interface ShareAppMessageContent {
2
4
  /** 分享标题 */
3
5
  title?: string;
@@ -36,10 +38,12 @@ export interface PageShareItem {
36
38
  }
37
39
  /** 后端返回的页面 key → 配置映射 */
38
40
  export type ShareConfigMap = Record<string, PageShareItem>;
39
- /** Adapter 注入接口 */
41
+ /**
42
+ * Adapter 注入接口 —— getConfig 支持「已解包」或「ThinkAdmin envelope」两种返回值。
43
+ * 业务方可以直接传 envelope-returning 接口:setConfigShare({ getConfig: getShareConfig })
44
+ */
40
45
  export interface ShareConfigAdapter {
41
- /** 拉取分享配置;返回 null 表示拉失败 */
42
- getConfig: () => Promise<ShareConfigMap | null>;
46
+ getConfig: () => Promise<AdapterPayload<ShareConfigMap>>;
43
47
  }
44
48
  /** 页面声明的兜底文案 / 跳转路径 */
45
49
  export interface PageShareFallback {
@@ -59,7 +63,7 @@ export interface SharePayload {
59
63
  /**
60
64
  * 注入业务回调(应用启动时调用一次;不调用则始终用 fallback)。
61
65
  */
62
- export declare function configShare(a: ShareConfigAdapter): void;
66
+ export declare function setConfigShare(a: ShareConfigAdapter): void;
63
67
  /**
64
68
  * 业务级分享配置 helper —— 返回 resolver,可直接喂 onShareAppMessage / onShareTimeline。
65
69
  * 后端配置异步加载,加载完成前先用 fallback、加载完后自动切到后端值。
package/dist/index.d.ts CHANGED
@@ -11,6 +11,6 @@ export * from './composables';
11
11
  export type { HlwMenuItem } from './components/hlw-menu/types';
12
12
  export type { HlwPagingRef, HlwPagingInstance } from './components/hlw-paging/types';
13
13
  export { useThemeStore } from './stores/theme';
14
- export { useApp, setupDefaultInterceptors } from './app';
14
+ export { useApp, setupInterceptors } from './app';
15
15
  export { hlw, type HlwInstance } from './hlw';
16
16
  export { vCopy } from './directives';
package/dist/index.js CHANGED
@@ -619,6 +619,15 @@ var __publicField = (obj, key, value) => {
619
619
  }
620
620
  return { date, fileSize, phone, money };
621
621
  }
622
+ function unwrapPayload(raw) {
623
+ if (raw == null)
624
+ return null;
625
+ if (typeof raw === "object" && "code" in raw && typeof raw.code === "number") {
626
+ const env = raw;
627
+ return env.code === 1 && env.data ? env.data : null;
628
+ }
629
+ return raw;
630
+ }
622
631
  const EMPTY = {
623
632
  banner_unit_id: "",
624
633
  grid_unit_id: "",
@@ -628,7 +637,7 @@ var __publicField = (obj, key, value) => {
628
637
  popup_unit_id: ""
629
638
  };
630
639
  let adapter$1 = null;
631
- function configAd(a) {
640
+ function setConfigAd(a) {
632
641
  adapter$1 = a;
633
642
  }
634
643
  const useAdStore = pinia.defineStore("hlw_ad", () => {
@@ -647,11 +656,11 @@ var __publicField = (obj, key, value) => {
647
656
  if (loaded.value && !force)
648
657
  return;
649
658
  if (!(adapter$1 == null ? void 0 : adapter$1.getConfig)) {
650
- console.warn("[useAd] adapter.getConfig 未注入;先调用 configAd()");
659
+ console.warn("[useAd] adapter.getConfig 未注入;先调用 setConfigAd()");
651
660
  return;
652
661
  }
653
662
  try {
654
- const cfg = await adapter$1.getConfig();
663
+ const cfg = unwrapPayload(await adapter$1.getConfig());
655
664
  if (cfg) {
656
665
  store.config = cfg;
657
666
  store.loaded = true;
@@ -877,7 +886,7 @@ var __publicField = (obj, key, value) => {
877
886
  let shareAdapter = null;
878
887
  const shareCache = vue.ref(null);
879
888
  let sharePending = null;
880
- function configShare(a) {
889
+ function setConfigShare(a) {
881
890
  shareAdapter = a;
882
891
  }
883
892
  function loadShareConfig() {
@@ -886,10 +895,11 @@ var __publicField = (obj, key, value) => {
886
895
  if (sharePending)
887
896
  return sharePending;
888
897
  if (!(shareAdapter == null ? void 0 : shareAdapter.getConfig)) {
889
- console.warn("[useShareConfig] adapter.getConfig 未注入;先调用 configShare()");
898
+ console.warn("[useShareConfig] adapter.getConfig 未注入;先调用 setConfigShare()");
890
899
  return Promise.resolve();
891
900
  }
892
- sharePending = shareAdapter.getConfig().then((cfg) => {
901
+ sharePending = shareAdapter.getConfig().then((raw) => {
902
+ const cfg = unwrapPayload(raw);
893
903
  if (cfg)
894
904
  shareCache.value = cfg;
895
905
  }).catch((e) => {
@@ -915,7 +925,7 @@ var __publicField = (obj, key, value) => {
915
925
  let adapter = null;
916
926
  const config = vue.ref(null);
917
927
  let pending = null;
918
- function configContact(a) {
928
+ function setConfigContact(a) {
919
929
  adapter = a;
920
930
  }
921
931
  function loadConfig() {
@@ -924,10 +934,11 @@ var __publicField = (obj, key, value) => {
924
934
  if (pending)
925
935
  return pending;
926
936
  if (!(adapter == null ? void 0 : adapter.getConfig)) {
927
- console.warn("[useContact] adapter.getConfig 未注入;先调用 configContact()");
937
+ console.warn("[useContact] adapter.getConfig 未注入;先调用 setConfigContact()");
928
938
  return Promise.resolve();
929
939
  }
930
- pending = adapter.getConfig().then((cfg) => {
940
+ pending = adapter.getConfig().then((raw) => {
941
+ const cfg = unwrapPayload(raw);
931
942
  if (cfg)
932
943
  config.value = cfg;
933
944
  }).catch((e) => {
@@ -1774,7 +1785,7 @@ var __publicField = (obj, key, value) => {
1774
1785
  }
1775
1786
  }
1776
1787
  let _sigSecret = "";
1777
- function setupDefaultInterceptors(options = {}) {
1788
+ function setupInterceptors(options = {}) {
1778
1789
  const opts = { ..._defaultOpts, ...options };
1779
1790
  if (opts.sigSecret)
1780
1791
  _sigSecret = opts.sigSecret;
@@ -1835,7 +1846,7 @@ var __publicField = (obj, key, value) => {
1835
1846
  const offError = http.onError((err) => {
1836
1847
  var _a;
1837
1848
  if (err.message.includes("401")) {
1838
- (_a = opts.onUnauthorized) == null ? void 0 : _a.call(opts);
1849
+ (_a = opts.onLogout) == null ? void 0 : _a.call(opts);
1839
1850
  }
1840
1851
  });
1841
1852
  _interceptorCleanup = [offRequest, offResponse, offError];
@@ -1889,9 +1900,6 @@ var __publicField = (obj, key, value) => {
1889
1900
  exports2.alistAdapter = alistAdapter;
1890
1901
  exports2.buildThemeStyle = buildThemeStyle;
1891
1902
  exports2.clearDeviceCache = clearDeviceCache;
1892
- exports2.configAd = configAd;
1893
- exports2.configContact = configContact;
1894
- exports2.configShare = configShare;
1895
1903
  exports2.confirmReward = confirmReward;
1896
1904
  exports2.cosAdapter = cosAdapter;
1897
1905
  exports2.destroyAds = destroyAds;
@@ -1910,7 +1918,10 @@ var __publicField = (obj, key, value) => {
1910
1918
  exports2.ossAdapter = ossAdapter;
1911
1919
  exports2.qiniuAdapter = qiniuAdapter;
1912
1920
  exports2.resolveAppearance = resolveAppearance;
1913
- exports2.setupDefaultInterceptors = setupDefaultInterceptors;
1921
+ exports2.setConfigAd = setConfigAd;
1922
+ exports2.setConfigContact = setConfigContact;
1923
+ exports2.setConfigShare = setConfigShare;
1924
+ exports2.setupInterceptors = setupInterceptors;
1914
1925
  exports2.useAd = useAd;
1915
1926
  exports2.useApp = useApp;
1916
1927
  exports2.useColor = useColor;
package/dist/index.mjs CHANGED
@@ -618,6 +618,15 @@ function useFormat() {
618
618
  }
619
619
  return { date, fileSize, phone, money };
620
620
  }
621
+ function unwrapPayload(raw) {
622
+ if (raw == null)
623
+ return null;
624
+ if (typeof raw === "object" && "code" in raw && typeof raw.code === "number") {
625
+ const env = raw;
626
+ return env.code === 1 && env.data ? env.data : null;
627
+ }
628
+ return raw;
629
+ }
621
630
  const EMPTY = {
622
631
  banner_unit_id: "",
623
632
  grid_unit_id: "",
@@ -627,7 +636,7 @@ const EMPTY = {
627
636
  popup_unit_id: ""
628
637
  };
629
638
  let adapter$1 = null;
630
- function configAd(a) {
639
+ function setConfigAd(a) {
631
640
  adapter$1 = a;
632
641
  }
633
642
  const useAdStore = defineStore("hlw_ad", () => {
@@ -646,11 +655,11 @@ function useAd() {
646
655
  if (loaded.value && !force)
647
656
  return;
648
657
  if (!(adapter$1 == null ? void 0 : adapter$1.getConfig)) {
649
- console.warn("[useAd] adapter.getConfig 未注入;先调用 configAd()");
658
+ console.warn("[useAd] adapter.getConfig 未注入;先调用 setConfigAd()");
650
659
  return;
651
660
  }
652
661
  try {
653
- const cfg = await adapter$1.getConfig();
662
+ const cfg = unwrapPayload(await adapter$1.getConfig());
654
663
  if (cfg) {
655
664
  store.config = cfg;
656
665
  store.loaded = true;
@@ -876,7 +885,7 @@ function useShare(config2) {
876
885
  let shareAdapter = null;
877
886
  const shareCache = ref(null);
878
887
  let sharePending = null;
879
- function configShare(a) {
888
+ function setConfigShare(a) {
880
889
  shareAdapter = a;
881
890
  }
882
891
  function loadShareConfig() {
@@ -885,10 +894,11 @@ function loadShareConfig() {
885
894
  if (sharePending)
886
895
  return sharePending;
887
896
  if (!(shareAdapter == null ? void 0 : shareAdapter.getConfig)) {
888
- console.warn("[useShareConfig] adapter.getConfig 未注入;先调用 configShare()");
897
+ console.warn("[useShareConfig] adapter.getConfig 未注入;先调用 setConfigShare()");
889
898
  return Promise.resolve();
890
899
  }
891
- sharePending = shareAdapter.getConfig().then((cfg) => {
900
+ sharePending = shareAdapter.getConfig().then((raw) => {
901
+ const cfg = unwrapPayload(raw);
892
902
  if (cfg)
893
903
  shareCache.value = cfg;
894
904
  }).catch((e) => {
@@ -914,7 +924,7 @@ function useShareConfig(pageKey, fallback) {
914
924
  let adapter = null;
915
925
  const config = ref(null);
916
926
  let pending = null;
917
- function configContact(a) {
927
+ function setConfigContact(a) {
918
928
  adapter = a;
919
929
  }
920
930
  function loadConfig() {
@@ -923,10 +933,11 @@ function loadConfig() {
923
933
  if (pending)
924
934
  return pending;
925
935
  if (!(adapter == null ? void 0 : adapter.getConfig)) {
926
- console.warn("[useContact] adapter.getConfig 未注入;先调用 configContact()");
936
+ console.warn("[useContact] adapter.getConfig 未注入;先调用 setConfigContact()");
927
937
  return Promise.resolve();
928
938
  }
929
- pending = adapter.getConfig().then((cfg) => {
939
+ pending = adapter.getConfig().then((raw) => {
940
+ const cfg = unwrapPayload(raw);
930
941
  if (cfg)
931
942
  config.value = cfg;
932
943
  }).catch((e) => {
@@ -1773,7 +1784,7 @@ function buildSignString(url) {
1773
1784
  }
1774
1785
  }
1775
1786
  let _sigSecret = "";
1776
- function setupDefaultInterceptors(options = {}) {
1787
+ function setupInterceptors(options = {}) {
1777
1788
  const opts = { ..._defaultOpts, ...options };
1778
1789
  if (opts.sigSecret)
1779
1790
  _sigSecret = opts.sigSecret;
@@ -1834,7 +1845,7 @@ function setupDefaultInterceptors(options = {}) {
1834
1845
  const offError = http.onError((err) => {
1835
1846
  var _a;
1836
1847
  if (err.message.includes("401")) {
1837
- (_a = opts.onUnauthorized) == null ? void 0 : _a.call(opts);
1848
+ (_a = opts.onLogout) == null ? void 0 : _a.call(opts);
1838
1849
  }
1839
1850
  });
1840
1851
  _interceptorCleanup = [offRequest, offResponse, offError];
@@ -1889,9 +1900,6 @@ export {
1889
1900
  alistAdapter,
1890
1901
  buildThemeStyle,
1891
1902
  clearDeviceCache,
1892
- configAd,
1893
- configContact,
1894
- configShare,
1895
1903
  confirmReward,
1896
1904
  cosAdapter,
1897
1905
  destroyAds,
@@ -1910,7 +1918,10 @@ export {
1910
1918
  ossAdapter,
1911
1919
  qiniuAdapter,
1912
1920
  resolveAppearance,
1913
- setupDefaultInterceptors,
1921
+ setConfigAd,
1922
+ setConfigContact,
1923
+ setConfigShare,
1924
+ setupInterceptors,
1914
1925
  useAd,
1915
1926
  useApp,
1916
1927
  useColor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hlw-uni/mp-vue",
3
- "version": "2.0.0",
3
+ "version": "2.0.4",
4
4
  "description": "hlw-uni 小程序运行时 — Vue 组件 + composables + theme + http + 工具集(合并自原 mp-core)",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/app.ts CHANGED
@@ -20,7 +20,7 @@ export interface InterceptorOptions {
20
20
  /** Token 来源函数 */
21
21
  getToken?: () => string;
22
22
  /** 登录失效时的处理函数 */
23
- onUnauthorized?: () => void;
23
+ onLogout?: () => void;
24
24
  /** 接口业务错误码是否自动 toast */
25
25
  autoToastError?: boolean;
26
26
  }
@@ -90,7 +90,7 @@ let _sigSecret = '';
90
90
  /**
91
91
  * 注册默认请求、响应和错误拦截器。
92
92
  */
93
- export function setupDefaultInterceptors(options: InterceptorOptions & { sigSecret?: string } = {}) {
93
+ export function setupInterceptors(options: InterceptorOptions & { sigSecret?: string } = {}) {
94
94
  const opts = { ..._defaultOpts, ...options };
95
95
  if (opts.sigSecret) _sigSecret = opts.sigSecret;
96
96
  if (opts.baseURL) http.setBaseURL(opts.baseURL);
@@ -165,7 +165,7 @@ export function setupDefaultInterceptors(options: InterceptorOptions & { sigSecr
165
165
  */
166
166
  const offError = http.onError((err: Error) => {
167
167
  if (err.message.includes('401')) {
168
- opts.onUnauthorized?.();
168
+ opts.onLogout?.();
169
169
  }
170
170
  });
171
171
 
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Adapter 返回值解包:兼容业务方传「已解包 T」或「ThinkAdmin envelope { code, data }」。
3
+ *
4
+ * 业务方写法:
5
+ * setConfigAd({ getConfig: getAdConfig }) // 直接传 envelope-returning 函数
6
+ * setConfigAd({ getConfig: async () => myUnwrapped }) // 也支持已解包
7
+ *
8
+ * 鸭子类型识别:raw 是对象且有 number 类型的 code 字段 → 当 envelope 处理。
9
+ */
10
+ export type AdapterPayload<T> = T | null | { code: number; data?: T; info?: string };
11
+
12
+ export function unwrapPayload<T>(raw: AdapterPayload<T>): T | null {
13
+ if (raw == null) return null;
14
+ if (typeof raw === "object" && "code" in raw && typeof (raw as { code: unknown }).code === "number") {
15
+ const env = raw as { code: number; data?: T };
16
+ return env.code === 1 && env.data ? env.data : null;
17
+ }
18
+ return raw as T;
19
+ }
@@ -11,11 +11,11 @@
11
11
  *
12
12
  * 1. 在 App.vue 注入「全局」回调(拿配置 + 校验登录):
13
13
  *
14
- * import { configAd } from "@hlw-uni/mp-vue";
14
+ * import { setConfigAd } from "@hlw-uni/mp-vue";
15
15
  * import { getAdConfig } from "@/api/ad";
16
16
  * import { useUserStore } from "@/store";
17
17
  *
18
- * configAd({
18
+ * setConfigAd({
19
19
  * getConfig: async () => {
20
20
  * const res = await getAdConfig();
21
21
  * return res.code === 1 && res.data ? res.data : null;
@@ -45,6 +45,7 @@
45
45
  import { defineStore, storeToRefs } from "pinia";
46
46
  import { ref } from "vue";
47
47
  import { useMsg, type HlwMsg } from "../msg";
48
+ import { unwrapPayload, type AdapterPayload } from "../_internal/unwrap";
48
49
 
49
50
  /** 6 种广告类型 */
50
51
  export type AdType = "banner" | "grid" | "custom" | "video" | "reward" | "popup";
@@ -65,10 +66,18 @@ export interface AdError {
65
66
  errMsg: string;
66
67
  }
67
68
 
68
- /** 业务回调注入接口 —— configAd 时由项目提供 */
69
+ /**
70
+ * 业务回调注入接口 —— setConfigAd 时由项目提供。
71
+ *
72
+ * getConfig 支持两种返回:
73
+ * - 已解包:直接返回 AdConfig 或 null
74
+ * - ThinkAdmin envelope:返回 { code, data } 对象,库自动按 code===1 解包
75
+ *
76
+ * 业务方可以直接传 envelope-returning 的接口函数引用:
77
+ * setConfigAd({ getConfig: getAdConfig })
78
+ */
69
79
  export interface AdAdapter {
70
- /** 拉取广告配置;返回 null 表示拉失败,store 不更新 */
71
- getConfig: () => Promise<AdConfig | null>;
80
+ getConfig: () => Promise<AdapterPayload<AdConfig>>;
72
81
  /** 是否已登录;不传 = 不校验(showReward 调用前会问一次) */
73
82
  isAuth?: () => boolean;
74
83
  }
@@ -114,7 +123,7 @@ let adapter: AdAdapter | null = null;
114
123
  * 注入业务回调,应用启动时调用一次。
115
124
  * 不调用也不会崩,但 loadConfig / showReward 会无效。
116
125
  */
117
- export function configAd(a: AdAdapter): void {
126
+ export function setConfigAd(a: AdAdapter): void {
118
127
  adapter = a;
119
128
  }
120
129
 
@@ -151,11 +160,11 @@ export function useAd() {
151
160
  async function loadConfig(force = false): Promise<void> {
152
161
  if (loaded.value && !force) return;
153
162
  if (!adapter?.getConfig) {
154
- console.warn("[useAd] adapter.getConfig 未注入;先调用 configAd()");
163
+ console.warn("[useAd] adapter.getConfig 未注入;先调用 setConfigAd()");
155
164
  return;
156
165
  }
157
166
  try {
158
- const cfg = await adapter.getConfig();
167
+ const cfg = unwrapPayload(await adapter.getConfig());
159
168
  if (cfg) {
160
169
  store.config = cfg;
161
170
  store.loaded = true;
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * 1. App.vue / bootstrap 注入回调:
9
9
  *
10
- * configContact({
10
+ * setConfigContact({
11
11
  * getConfig: async () => {
12
12
  * const res = await getContactConfig();
13
13
  * return res.code === 1 ? res.data : null;
@@ -20,6 +20,7 @@
20
20
  * <hlw-button open-type="contact" v-bind="contact">联系客服</hlw-button>
21
21
  */
22
22
  import { computed, ref } from "vue";
23
+ import { unwrapPayload, type AdapterPayload } from "../_internal/unwrap";
23
24
 
24
25
  /** 后端返回的客服配置(按 button open-type=contact 标准属性命名) */
25
26
  export interface ContactConfig {
@@ -29,10 +30,12 @@ export interface ContactConfig {
29
30
  show_message_card: boolean;
30
31
  }
31
32
 
32
- /** Adapter 注入接口 */
33
+ /**
34
+ * Adapter 注入接口 —— getConfig 支持「已解包」或「ThinkAdmin envelope」两种返回。
35
+ * 业务方可以直接传 envelope-returning 接口:setConfigContact({ getConfig: getContactConfig })
36
+ */
33
37
  export interface ContactAdapter {
34
- /** 拉取客服配置;返回 null 表示拉失败,store 不更新 */
35
- getConfig: () => Promise<ContactConfig | null>;
38
+ getConfig: () => Promise<AdapterPayload<ContactConfig>>;
36
39
  }
37
40
 
38
41
  /** v-bind 到 button 的 camelCase props(微信原生属性约定) */
@@ -50,7 +53,7 @@ let pending: Promise<void> | null = null;
50
53
  /**
51
54
  * 注入业务回调(应用启动时调用一次;不调用则 useContact 始终返回空字段)。
52
55
  */
53
- export function configContact(a: ContactAdapter): void {
56
+ export function setConfigContact(a: ContactAdapter): void {
54
57
  adapter = a;
55
58
  }
56
59
 
@@ -58,11 +61,12 @@ function loadConfig(): Promise<void> {
58
61
  if (config.value) return Promise.resolve();
59
62
  if (pending) return pending;
60
63
  if (!adapter?.getConfig) {
61
- console.warn("[useContact] adapter.getConfig 未注入;先调用 configContact()");
64
+ console.warn("[useContact] adapter.getConfig 未注入;先调用 setConfigContact()");
62
65
  return Promise.resolve();
63
66
  }
64
67
  pending = adapter.getConfig()
65
- .then((cfg) => {
68
+ .then((raw) => {
69
+ const cfg = unwrapPayload(raw);
66
70
  if (cfg) config.value = cfg;
67
71
  })
68
72
  .catch((e) => {
@@ -12,7 +12,7 @@ export { useValidate } from "./validate";
12
12
  export { useFormat } from "./format";
13
13
  export {
14
14
  useAd,
15
- configAd,
15
+ setConfigAd,
16
16
  destroyAds,
17
17
  confirmReward,
18
18
  type AdType,
@@ -26,7 +26,7 @@ export {
26
26
  export {
27
27
  useShare,
28
28
  useShareConfig,
29
- configShare,
29
+ setConfigShare,
30
30
  type ShareConfig,
31
31
  type ShareConfigResolver,
32
32
  type ShareFrom,
@@ -40,7 +40,7 @@ export {
40
40
  } from "./share";
41
41
  export {
42
42
  useContact,
43
- configContact,
43
+ setConfigContact,
44
44
  type ContactConfig,
45
45
  type ContactAdapter,
46
46
  type ContactBindProps,
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app';
9
9
  import { ref } from 'vue';
10
+ import { unwrapPayload, type AdapterPayload } from '../_internal/unwrap';
10
11
 
11
12
  export interface ShareAppMessageContent {
12
13
  /** 分享标题 */
@@ -88,7 +89,7 @@ export function useShare(config: ShareConfig | ShareConfigResolver) {
88
89
  *
89
90
  * 1. App.vue / bootstrap 注入回调:
90
91
  *
91
- * configShare({
92
+ * setConfigShare({
92
93
  * getConfig: async () => {
93
94
  * const res = await getShareConfig();
94
95
  * return res.code === 1 ? res.data : null;
@@ -114,10 +115,12 @@ export interface PageShareItem {
114
115
  /** 后端返回的页面 key → 配置映射 */
115
116
  export type ShareConfigMap = Record<string, PageShareItem>;
116
117
 
117
- /** Adapter 注入接口 */
118
+ /**
119
+ * Adapter 注入接口 —— getConfig 支持「已解包」或「ThinkAdmin envelope」两种返回值。
120
+ * 业务方可以直接传 envelope-returning 接口:setConfigShare({ getConfig: getShareConfig })
121
+ */
118
122
  export interface ShareConfigAdapter {
119
- /** 拉取分享配置;返回 null 表示拉失败 */
120
- getConfig: () => Promise<ShareConfigMap | null>;
123
+ getConfig: () => Promise<AdapterPayload<ShareConfigMap>>;
121
124
  }
122
125
 
123
126
  /** 页面声明的兜底文案 / 跳转路径 */
@@ -144,7 +147,7 @@ let sharePending: Promise<void> | null = null;
144
147
  /**
145
148
  * 注入业务回调(应用启动时调用一次;不调用则始终用 fallback)。
146
149
  */
147
- export function configShare(a: ShareConfigAdapter): void {
150
+ export function setConfigShare(a: ShareConfigAdapter): void {
148
151
  shareAdapter = a;
149
152
  }
150
153
 
@@ -152,11 +155,12 @@ function loadShareConfig(): Promise<void> {
152
155
  if (shareCache.value) return Promise.resolve();
153
156
  if (sharePending) return sharePending;
154
157
  if (!shareAdapter?.getConfig) {
155
- console.warn("[useShareConfig] adapter.getConfig 未注入;先调用 configShare()");
158
+ console.warn("[useShareConfig] adapter.getConfig 未注入;先调用 setConfigShare()");
156
159
  return Promise.resolve();
157
160
  }
158
161
  sharePending = shareAdapter.getConfig()
159
- .then((cfg) => {
162
+ .then((raw) => {
163
+ const cfg = unwrapPayload(raw);
160
164
  if (cfg) shareCache.value = cfg;
161
165
  })
162
166
  .catch((e) => {
package/src/index.ts CHANGED
@@ -19,7 +19,7 @@ export type { HlwPagingRef, HlwPagingInstance } from "./components/hlw-paging/ty
19
19
  export { useThemeStore } from "./stores/theme";
20
20
 
21
21
  // App 根上下文
22
- export { useApp, setupDefaultInterceptors } from "./app";
22
+ export { useApp, setupInterceptors } from "./app";
23
23
 
24
24
  // hlw 全局命名空间
25
25
  export { hlw, type HlwInstance } from "./hlw";