km-card-layout-component-miniprogram 0.1.36 → 0.1.39

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.
@@ -56,7 +56,7 @@ Component({
56
56
  const data = this.data.rootData || {};
57
57
  const wrapperStyle = (0, index_1.styleObjectToString)((0, index_1.buildWrapperStyle)(el, "rpx"), "rpx");
58
58
  const contentStyle = (0, index_1.styleObjectToString)((0, index_1.buildTextContentStyle)(el, "rpx"), "rpx");
59
- const textValue = (0, index_1.getTextValue)(el, data);
59
+ const textValue = (0, index_1.getTextValue)(el, data, false);
60
60
  const textStyle = (0, index_1.styleObjectToString)({
61
61
  ...(0, index_1.buildTextContentStyle)(el, "rpx"),
62
62
  ...(0, index_1.buildTextValueStyle)(el, "rpx"),
@@ -87,11 +87,18 @@ Component({
87
87
  },
88
88
  lifetimes: {
89
89
  attached() {
90
- this.loadFont();
90
+ this.ensureFontReady();
91
91
  this.rebuild();
92
92
  },
93
93
  },
94
94
  methods: {
95
+ ensureFontReady() {
96
+ const self = this;
97
+ if (!self._fontReady) {
98
+ self._fontReady = this.loadFont().catch(() => undefined);
99
+ }
100
+ return self._fontReady;
101
+ },
95
102
  rebuild() {
96
103
  const data = normalizeMoreCompany(this.data.data);
97
104
  const layoutInput = (0, helpers_1.hasCompanyDutyKey)(this.data.layout)
@@ -107,10 +114,17 @@ Component({
107
114
  this.setData({ cards, rootData, firstCard: [cards[0]] });
108
115
  },
109
116
  async handleDrawCanvas(options) {
117
+ const self = this;
118
+ if (!(options === null || options === void 0 ? void 0 : options.skipDrawingGuard)) {
119
+ if (self._isDrawing)
120
+ return;
121
+ self._isDrawing = true;
122
+ }
110
123
  try {
111
124
  if ((options === null || options === void 0 ? void 0 : options.waitForReady) !== false) {
112
125
  await nextTick();
113
126
  }
127
+ await this.ensureFontReady();
114
128
  const canvas = this.selectComponent(`#layout-canvas`);
115
129
  await canvas.draw(this);
116
130
  const filePath = await canvas.toTempFilePath();
@@ -119,26 +133,40 @@ Component({
119
133
  catch (_a) {
120
134
  wx.showToast({ title: "保存失败", icon: "error" });
121
135
  }
136
+ finally {
137
+ if (!(options === null || options === void 0 ? void 0 : options.skipDrawingGuard)) {
138
+ self._isDrawing = false;
139
+ }
140
+ }
122
141
  },
123
142
  async handleDrawShareCanvas() {
124
143
  var _a, _b, _c;
125
- // setData / 组件渲染完成
126
- await nextTick();
127
- const layoutPath = await this.handleDrawCanvas({ waitForReady: false });
128
- if (!layoutPath)
129
- return;
130
- const shareCanvas = this.selectComponent(`#share-canvas`);
131
- if (!shareCanvas || typeof shareCanvas.drawShare !== "function")
144
+ const self = this;
145
+ if (self._isDrawing)
132
146
  return;
133
- const shareStyleId = (_c = (_b = (_a = this.data.data) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.shareStyle) === null || _c === void 0 ? void 0 : _c.coverStyleId;
134
- const filePath = await shareCanvas.drawShare({
135
- layoutPath,
136
- shareStyleId,
137
- });
138
- if (filePath) {
139
- this.setData({ shareImage: filePath });
147
+ self._isDrawing = true;
148
+ try {
149
+ // wait for setData / component render
150
+ await nextTick();
151
+ const layoutPath = await this.handleDrawCanvas({ waitForReady: false, skipDrawingGuard: true });
152
+ if (!layoutPath)
153
+ return;
154
+ const shareCanvas = this.selectComponent(`#share-canvas`);
155
+ if (!shareCanvas || typeof shareCanvas.drawShare !== "function")
156
+ return;
157
+ const shareStyleId = (_c = (_b = (_a = this.data.data) === null || _a === void 0 ? void 0 : _a.user) === null || _b === void 0 ? void 0 : _b.shareStyle) === null || _c === void 0 ? void 0 : _c.coverStyleId;
158
+ const filePath = await shareCanvas.drawShare({
159
+ layoutPath,
160
+ shareStyleId,
161
+ });
162
+ if (filePath) {
163
+ this.setData({ shareImage: filePath });
164
+ }
165
+ return filePath;
166
+ }
167
+ finally {
168
+ self._isDrawing = false;
140
169
  }
141
- return filePath;
142
170
  },
143
171
  async loadFont() {
144
172
  await wx.loadFontFace({
@@ -11,6 +11,29 @@ const resolveShareStyle = (shareStyleId) => {
11
11
  return share_style_1.ShareStyle[0];
12
12
  };
13
13
  const resolveShareBackground = (style) => (style === null || style === void 0 ? void 0 : style.bigImg) || (style === null || style === void 0 ? void 0 : style.path) || (style === null || style === void 0 ? void 0 : style.smallImg) || "";
14
+ const nextTick = () => new Promise((resolve) => {
15
+ wx.nextTick(() => resolve());
16
+ });
17
+ const sleep = (ms) => new Promise((resolve) => {
18
+ setTimeout(resolve, ms);
19
+ });
20
+ const imageInfoCache = new Map();
21
+ const preloadImage = (src) => {
22
+ if (!src)
23
+ return Promise.resolve();
24
+ const cached = imageInfoCache.get(src);
25
+ if (cached)
26
+ return cached;
27
+ const promise = new Promise((resolve) => {
28
+ wx.getImageInfo({
29
+ src,
30
+ success: () => resolve(),
31
+ fail: () => resolve(),
32
+ });
33
+ });
34
+ imageInfoCache.set(src, promise);
35
+ return promise;
36
+ };
14
37
  Component({
15
38
  data: {
16
39
  shareBgImage: "",
@@ -24,7 +47,32 @@ Component({
24
47
  this.setData(data, resolve);
25
48
  });
26
49
  },
50
+ getRects(selector) {
51
+ return new Promise((resolve) => {
52
+ const query = this.createSelectorQuery();
53
+ query.selectAll(selector).boundingClientRect();
54
+ query.exec((res) => {
55
+ resolve((res && res[0]) || []);
56
+ });
57
+ });
58
+ },
59
+ async waitForImagesReady(srcs) {
60
+ await nextTick();
61
+ await Promise.all(srcs.map((src) => preloadImage(src)));
62
+ for (let i = 0; i < 10; i += 1) {
63
+ const rects = await this.getRects(".share-container image");
64
+ if (rects.length === 0
65
+ || rects.every((rect) => rect && rect.width > 0 && rect.height > 0)) {
66
+ return;
67
+ }
68
+ await sleep(50);
69
+ }
70
+ },
27
71
  async drawShare(options) {
72
+ const self = this;
73
+ if (self._isDrawing)
74
+ return;
75
+ self._isDrawing = true;
28
76
  try {
29
77
  const { layoutPath, shareStyleId } = options || {};
30
78
  if (!layoutPath)
@@ -41,6 +89,7 @@ Component({
41
89
  shareOffsetY,
42
90
  shareImage: layoutPath,
43
91
  });
92
+ await this.waitForImagesReady([shareBgImage, layoutPath]);
44
93
  const canvas = this.selectComponent(`#share-canvas`);
45
94
  if (!canvas)
46
95
  return;
@@ -51,6 +100,9 @@ Component({
51
100
  catch (_a) {
52
101
  wx.showToast({ title: "保存失败", icon: "error" });
53
102
  }
103
+ finally {
104
+ self._isDrawing = false;
105
+ }
54
106
  },
55
107
  },
56
108
  });
@@ -6,6 +6,6 @@
6
6
  mode="widthFix"
7
7
  style="margin-left: {{shareOffsetX}}rpx; margin-top: {{shareOffsetY}}rpx;"
8
8
  />
9
- <image class="share-bg share-item" src="{{shareBgImage}}" />
9
+ <image class="share-bg share-item" src="{{shareBgImage}}" mode="aspectFill" />
10
10
  </view>
11
11
  <wxml2canvas id="share-canvas" container-class="share-container" item-class="share-item"></wxml2canvas>
@@ -129,7 +129,7 @@ const buildImageContentStyle = (el, unit = 'px') => {
129
129
  return style;
130
130
  };
131
131
  exports.buildImageContentStyle = buildImageContentStyle;
132
- const getTextValue = (el, data) => {
132
+ const getTextValue = (el, data, useDefault = true) => {
133
133
  // 如果 id 匹配,则使用 mock 的 binding,否则用原来的
134
134
  const binding = el.id === String(exports.mockBaseCompanyDutyTemplateItem.id)
135
135
  ? exports.mockBaseCompanyDutyTemplateItem.bind
@@ -137,15 +137,21 @@ const getTextValue = (el, data) => {
137
137
  const bound = binding && data ? (0, data_1.resolveBindingValue)(binding, data) : undefined;
138
138
  const val = bound !== undefined && bound !== null
139
139
  ? bound
140
- : el.defaultValue !== undefined && el.defaultValue !== null
140
+ : useDefault && el.defaultValue !== undefined && el.defaultValue !== null
141
141
  ? el.defaultValue
142
142
  : '';
143
143
  return `${val !== null && val !== void 0 ? val : ''}`;
144
144
  };
145
145
  exports.getTextValue = getTextValue;
146
- const getImageSrc = (el, data) => {
146
+ const getImageSrc = (el, data, useDefault = true) => {
147
147
  const bound = el.binding && data ? (0, data_1.resolveBindingValue)(el.binding, data) : undefined;
148
- return bound || el.defaultUrl || el.defaultValue || '';
148
+ if (bound) {
149
+ return bound;
150
+ }
151
+ if (useDefault) {
152
+ return el.defaultUrl || el.defaultValue || '';
153
+ }
154
+ return '';
149
155
  };
150
156
  exports.getImageSrc = getImageSrc;
151
157
  const getIconName = (el) => (el.name || el.defaultValue || '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "km-card-layout-component-miniprogram",
3
- "version": "0.1.36",
3
+ "version": "0.1.39",
4
4
  "description": "",
5
5
  "main": "miniprogram_dist/index.js",
6
6
  "miniprogram": "miniprogram_dist",
@@ -62,7 +62,7 @@ Component({
62
62
  const data = this.data.rootData || {};
63
63
  const wrapperStyle = styleObjectToString(buildWrapperStyle(el, "rpx"), "rpx");
64
64
  const contentStyle = styleObjectToString(buildTextContentStyle(el, "rpx"), "rpx");
65
- const textValue = getTextValue(el, data);
65
+ const textValue = getTextValue(el, data, false);
66
66
  const textStyle = styleObjectToString(
67
67
  {
68
68
  ...buildTextContentStyle(el, "rpx"),
@@ -116,11 +116,18 @@ Component({
116
116
  },
117
117
  lifetimes: {
118
118
  attached() {
119
- this.loadFont();
119
+ this.ensureFontReady();
120
120
  this.rebuild();
121
121
  },
122
122
  },
123
123
  methods: {
124
+ ensureFontReady() {
125
+ const self = this as any;
126
+ if (!self._fontReady) {
127
+ self._fontReady = this.loadFont().catch(() => undefined);
128
+ }
129
+ return self._fontReady;
130
+ },
124
131
  rebuild() {
125
132
  const data = normalizeMoreCompany(this.data.data);
126
133
  const layoutInput = hasCompanyDutyKey(this.data.layout)
@@ -136,11 +143,17 @@ Component({
136
143
  this.setData({ cards, rootData, firstCard: [cards[0]] });
137
144
  },
138
145
 
139
- async handleDrawCanvas(options?: { waitForReady?: boolean }) {
146
+ async handleDrawCanvas(options?: { waitForReady?: boolean; skipDrawingGuard?: boolean }) {
147
+ const self = this as any;
148
+ if (!options?.skipDrawingGuard) {
149
+ if (self._isDrawing) return;
150
+ self._isDrawing = true;
151
+ }
140
152
  try {
141
153
  if (options?.waitForReady !== false) {
142
154
  await nextTick();
143
155
  }
156
+ await this.ensureFontReady();
144
157
 
145
158
  const canvas = this.selectComponent(`#layout-canvas`);
146
159
  await canvas.draw(this);
@@ -148,29 +161,40 @@ Component({
148
161
  return filePath;
149
162
  } catch {
150
163
  wx.showToast({ title: "保存失败", icon: "error" });
164
+ } finally {
165
+ if (!options?.skipDrawingGuard) {
166
+ self._isDrawing = false;
167
+ }
151
168
  }
152
169
  },
153
170
 
154
171
  async handleDrawShareCanvas() {
155
- // setData / 组件渲染完成
156
- await nextTick();
172
+ const self = this as any;
173
+ if (self._isDrawing) return;
174
+ self._isDrawing = true;
175
+ try {
176
+ // wait for setData / component render
177
+ await nextTick();
157
178
 
158
- const layoutPath = await this.handleDrawCanvas({ waitForReady: false });
159
- if (!layoutPath) return;
179
+ const layoutPath = await this.handleDrawCanvas({ waitForReady: false, skipDrawingGuard: true });
180
+ if (!layoutPath) return;
160
181
 
161
- const shareCanvas = this.selectComponent(`#share-canvas`) as any;
162
- if (!shareCanvas || typeof shareCanvas.drawShare !== "function") return;
182
+ const shareCanvas = this.selectComponent(`#share-canvas`) as any;
183
+ if (!shareCanvas || typeof shareCanvas.drawShare !== "function") return;
163
184
 
164
- const shareStyleId = this.data.data?.user?.shareStyle?.coverStyleId;
165
- const filePath = await shareCanvas.drawShare({
166
- layoutPath,
167
- shareStyleId,
168
- });
185
+ const shareStyleId = this.data.data?.user?.shareStyle?.coverStyleId;
186
+ const filePath = await shareCanvas.drawShare({
187
+ layoutPath,
188
+ shareStyleId,
189
+ });
169
190
 
170
- if (filePath) {
171
- this.setData({ shareImage: filePath });
191
+ if (filePath) {
192
+ this.setData({ shareImage: filePath });
193
+ }
194
+ return filePath;
195
+ } finally {
196
+ self._isDrawing = false;
172
197
  }
173
- return filePath;
174
198
  },
175
199
 
176
200
  async loadFont() {
@@ -14,6 +14,33 @@ const resolveShareStyle = (shareStyleId?: number | string): ShareStyleItem => {
14
14
  const resolveShareBackground = (style?: ShareStyleItem) =>
15
15
  style?.bigImg || style?.path || style?.smallImg || "";
16
16
 
17
+ const nextTick = () =>
18
+ new Promise<void>((resolve) => {
19
+ wx.nextTick(() => resolve());
20
+ });
21
+
22
+ const sleep = (ms: number) =>
23
+ new Promise<void>((resolve) => {
24
+ setTimeout(resolve, ms);
25
+ });
26
+
27
+ const imageInfoCache = new Map<string, Promise<void>>();
28
+
29
+ const preloadImage = (src?: string) => {
30
+ if (!src) return Promise.resolve();
31
+ const cached = imageInfoCache.get(src);
32
+ if (cached) return cached;
33
+ const promise = new Promise<void>((resolve) => {
34
+ wx.getImageInfo({
35
+ src,
36
+ success: () => resolve(),
37
+ fail: () => resolve(),
38
+ });
39
+ });
40
+ imageInfoCache.set(src, promise);
41
+ return promise;
42
+ };
43
+
17
44
  Component({
18
45
  data: {
19
46
  shareBgImage: "",
@@ -27,8 +54,34 @@ Component({
27
54
  this.setData(data, resolve);
28
55
  });
29
56
  },
57
+ getRects(selector: string) {
58
+ return new Promise<any[]>((resolve) => {
59
+ const query = this.createSelectorQuery();
60
+ query.selectAll(selector).boundingClientRect();
61
+ query.exec((res) => {
62
+ resolve((res && res[0]) || []);
63
+ });
64
+ });
65
+ },
66
+ async waitForImagesReady(srcs: Array<string | undefined>) {
67
+ await nextTick();
68
+ await Promise.all(srcs.map((src) => preloadImage(src)));
69
+ for (let i = 0; i < 10; i += 1) {
70
+ const rects = await this.getRects(".share-container image");
71
+ if (
72
+ rects.length === 0
73
+ || rects.every((rect) => rect && rect.width > 0 && rect.height > 0)
74
+ ) {
75
+ return;
76
+ }
77
+ await sleep(50);
78
+ }
79
+ },
30
80
 
31
81
  async drawShare(options: { layoutPath: string; shareStyleId?: number | string }) {
82
+ const self = this as any;
83
+ if (self._isDrawing) return;
84
+ self._isDrawing = true;
32
85
  try {
33
86
  const { layoutPath, shareStyleId } = options || {};
34
87
  if (!layoutPath) return;
@@ -45,6 +98,7 @@ Component({
45
98
  shareOffsetY,
46
99
  shareImage: layoutPath,
47
100
  });
101
+ await this.waitForImagesReady([shareBgImage, layoutPath]);
48
102
 
49
103
  const canvas = this.selectComponent(`#share-canvas`);
50
104
  if (!canvas) return;
@@ -53,6 +107,8 @@ Component({
53
107
  return filePath;
54
108
  } catch {
55
109
  wx.showToast({ title: "保存失败", icon: "error" });
110
+ } finally {
111
+ self._isDrawing = false;
56
112
  }
57
113
  },
58
114
  },
@@ -6,6 +6,6 @@
6
6
  mode="widthFix"
7
7
  style="margin-left: {{shareOffsetX}}rpx; margin-top: {{shareOffsetY}}rpx;"
8
8
  />
9
- <image class="share-bg share-item" src="{{shareBgImage}}" />
9
+ <image class="share-bg share-item" src="{{shareBgImage}}" mode="aspectFill" />
10
10
  </view>
11
11
  <wxml2canvas id="share-canvas" container-class="share-container" item-class="share-item"></wxml2canvas>
@@ -31,8 +31,11 @@ export interface TextIconConfig {
31
31
  color?: string;
32
32
  gap?: number;
33
33
  align?: 'left' | 'right';
34
+ // 图标属性的开关值
34
35
  enable?: boolean;
35
36
  style?: 'fill' | 'dot' | 'line' | 'text';
37
+ // 控制图标属性区域是否显示(用户端)
38
+ visible:boolean;
36
39
  }
37
40
 
38
41
  export interface TextElement extends CardElementBase {
@@ -158,10 +158,10 @@ export const buildImageContentStyle = (
158
158
  : '0';
159
159
  return style;
160
160
  };
161
-
162
161
  export const getTextValue = (
163
162
  el: TextElement,
164
- data?: Record<string, any>
163
+ data?: Record<string, any>,
164
+ useDefault: boolean = true
165
165
  ) => {
166
166
  // 如果 id 匹配,则使用 mock 的 binding,否则用原来的
167
167
  const binding =
@@ -175,7 +175,7 @@ export const getTextValue = (
175
175
  const val =
176
176
  bound !== undefined && bound !== null
177
177
  ? bound
178
- : el.defaultValue !== undefined && el.defaultValue !== null
178
+ : useDefault && el.defaultValue !== undefined && el.defaultValue !== null
179
179
  ? el.defaultValue
180
180
  : '';
181
181
 
@@ -183,10 +183,23 @@ export const getTextValue = (
183
183
  };
184
184
 
185
185
 
186
- export const getImageSrc = (el: ImageElement, data?: Record<string, any>) => {
186
+ export const getImageSrc = (
187
+ el: ImageElement,
188
+ data?: Record<string, any>,
189
+ useDefault: boolean = true
190
+ ) => {
187
191
  const bound =
188
192
  el.binding && data ? resolveBindingValue(el.binding, data) : undefined;
189
- return (bound as any) || el.defaultUrl || el.defaultValue || '';
193
+
194
+ if (bound) {
195
+ return bound as any;
196
+ }
197
+
198
+ if (useDefault) {
199
+ return el.defaultUrl || el.defaultValue || '';
200
+ }
201
+
202
+ return '';
190
203
  };
191
204
 
192
205
  export const getIconName = (el: IconElement) =>
@@ -114,12 +114,14 @@ export function buildImageContentStyle(
114
114
 
115
115
  export function getTextValue(
116
116
  el: TextElement,
117
- data?: Record<string, any>
117
+ data?: Record<string, any>,
118
+ useDefault?: boolean
118
119
  ): string;
119
120
 
120
121
  export function getImageSrc(
121
122
  el: ImageElement,
122
- data?: Record<string, any>
123
+ data?: Record<string, any>,
124
+ useDefault?: boolean
123
125
  ): string;
124
126
 
125
127
  export function getIconName(el: IconElement): string;