@mpxjs/webpack-plugin 2.10.3-beta.7 → 2.10.3-beta.9

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/lib/index.js CHANGED
@@ -1806,72 +1806,92 @@ try {
1806
1806
  normalModuleFactory.hooks.afterResolve.tap('MpxWebpackPlugin', ({ createData }) => {
1807
1807
  const { queryObj } = parseRequest(createData.request)
1808
1808
  const loaders = createData.loaders
1809
- if (queryObj.mpx && queryObj.mpx !== MPX_PROCESSED_FLAG) {
1810
- const type = queryObj.type
1809
+ const type = queryObj.type
1810
+ const mpxProcessFlag = queryObj.mpx && queryObj.mpx !== MPX_PROCESSED_FLAG
1811
+ const vueProcessFlag = queryObj.vue && queryObj.mpx !== MPX_PROCESSED_FLAG
1812
+ if (mpxProcessFlag || vueProcessFlag) {
1811
1813
  const extract = queryObj.extract
1812
-
1813
- if (type === 'styles') {
1814
- let insertBeforeIndex = -1
1815
- // 单次遍历收集所有索引
1814
+ if (type === 'styles' || type === 'style') {
1815
+ const loaderTypes = {
1816
+ 'node_modules/stylus-loader': -1,
1817
+ 'node_modules/sass-loader': -1,
1818
+ 'node_modules/less-loader': -1,
1819
+ 'node_modules/css-loader': -1
1820
+ }
1816
1821
  loaders.forEach((loader, index) => {
1817
1822
  const currentLoader = toPosix(loader.loader)
1818
- if (currentLoader.includes('node_modules/stylus-loader') || currentLoader.includes('node_modules/sass-loader') || currentLoader.includes('node_modules/less-loader')) {
1819
- insertBeforeIndex = index
1823
+ for (const key in loaderTypes) {
1824
+ if (currentLoader.includes(key)) {
1825
+ loaderTypes[key] = index
1826
+ break
1827
+ }
1820
1828
  }
1821
1829
  })
1830
+ const insertStripLoaders = (index) => {
1831
+ if (index !== -1) {
1832
+ loaders.splice(index, 0, { loader: styleStripConditionalPath, options: {id: 0} })
1833
+ loaders.splice(index + 2, 0, { loader: styleStripConditionalPath, options: {id: 1} })
1834
+ return true
1835
+ }
1836
+ return false
1837
+ }
1822
1838
 
1823
- if (insertBeforeIndex !== -1) {
1824
- loaders.splice(insertBeforeIndex, 0, { loader: styleStripConditionalPath })
1839
+ const priorities = ['node_modules/stylus-loader', 'node_modules/less-loader', 'node_modules/sass-loader', 'node_modules/css-loader']
1840
+ for (const loaderKey of priorities) {
1841
+ if (insertStripLoaders(loaderTypes[loaderKey])) {
1842
+ break
1843
+ }
1825
1844
  }
1826
- loaders.push({ loader: styleStripConditionalPath })
1827
1845
  }
1828
1846
 
1829
- switch (type) {
1830
- case 'styles':
1831
- case 'template': {
1832
- let insertBeforeIndex = -1
1833
- const info = typeLoaderProcessInfo[type]
1834
- loaders.forEach((loader, index) => {
1835
- const currentLoader = toPosix(loader.loader)
1836
- if (currentLoader.includes(info[0])) {
1837
- loader.loader = info[1]
1838
- insertBeforeIndex = index
1839
- } else if (currentLoader.includes(info[1])) {
1840
- insertBeforeIndex = index
1841
- }
1842
- })
1843
- if (insertBeforeIndex > -1) {
1844
- loaders.splice(insertBeforeIndex + 1, 0, {
1845
- loader: info[2]
1847
+ if (mpxProcessFlag) {
1848
+ switch (type) {
1849
+ case 'styles':
1850
+ case 'template': {
1851
+ let insertBeforeIndex = -1
1852
+ const info = typeLoaderProcessInfo[type]
1853
+ loaders.forEach((loader, index) => {
1854
+ const currentLoader = toPosix(loader.loader)
1855
+ if (currentLoader.includes(info[0])) {
1856
+ loader.loader = info[1]
1857
+ insertBeforeIndex = index
1858
+ } else if (currentLoader.includes(info[1])) {
1859
+ insertBeforeIndex = index
1860
+ }
1846
1861
  })
1862
+ if (insertBeforeIndex > -1) {
1863
+ loaders.splice(insertBeforeIndex + 1, 0, {
1864
+ loader: info[2]
1865
+ })
1866
+ }
1867
+ break
1847
1868
  }
1848
- break
1849
- }
1850
- case 'json':
1851
- if (queryObj.isTheme) {
1852
- loaders.unshift({
1853
- loader: jsonThemeCompilerPath
1854
- })
1855
- } else if (queryObj.isPlugin) {
1856
- loaders.unshift({
1857
- loader: jsonPluginCompilerPath
1858
- })
1859
- } else {
1869
+ case 'json':
1870
+ if (queryObj.isTheme) {
1871
+ loaders.unshift({
1872
+ loader: jsonThemeCompilerPath
1873
+ })
1874
+ } else if (queryObj.isPlugin) {
1875
+ loaders.unshift({
1876
+ loader: jsonPluginCompilerPath
1877
+ })
1878
+ } else {
1879
+ loaders.unshift({
1880
+ loader: jsonCompilerPath
1881
+ })
1882
+ }
1883
+ break
1884
+ case 'wxs':
1860
1885
  loaders.unshift({
1861
- loader: jsonCompilerPath
1886
+ loader: wxsLoaderPath
1862
1887
  })
1863
- }
1864
- break
1865
- case 'wxs':
1888
+ break
1889
+ }
1890
+ if (extract) {
1866
1891
  loaders.unshift({
1867
- loader: wxsLoaderPath
1892
+ loader: extractorPath
1868
1893
  })
1869
- break
1870
- }
1871
- if (extract) {
1872
- loaders.unshift({
1873
- loader: extractorPath
1874
- })
1894
+ }
1875
1895
  }
1876
1896
  createData.resource = addQuery(createData.resource, { mpx: MPX_PROCESSED_FLAG }, true)
1877
1897
  }
@@ -42,6 +42,7 @@ const wxs = require('./wxs')
42
42
  const component = require('./component')
43
43
  const fixComponentName = require('./fix-component-name')
44
44
  const rootPortal = require('./root-portal')
45
+ const titlebar = require('./titlebar')
45
46
 
46
47
  module.exports = function getComponentConfigs ({ warn, error }) {
47
48
  /**
@@ -125,6 +126,7 @@ module.exports = function getComponentConfigs ({ warn, error }) {
125
126
  hyphenTagName({ print }),
126
127
  label({ print }),
127
128
  component(),
128
- rootPortal({ print })
129
+ rootPortal({ print }),
130
+ titlebar({ print })
129
131
  ]
130
132
  }
@@ -0,0 +1,243 @@
1
+ <script>
2
+ import mpx from '@mpxjs/core'
3
+ export default {
4
+ name: 'mpx-titlebar',
5
+ props: {
6
+ // 来自 app.json 中 window 的 titlebar 相关配置
7
+ windowConfig: {
8
+ type: Object,
9
+ default: () => global.__mpxPageConfig
10
+ },
11
+ // 来自 页面 json 中的 titlebar 相关配置,会覆盖 windowConfig
12
+ pageConfig: {
13
+ type: Object,
14
+ default: () => ({})
15
+ }
16
+ },
17
+ computed: {
18
+ // 合并全局 window 配置与页面配置(页面配置覆盖全局配置)
19
+ cfg () {
20
+ return Object.assign({}, this.windowConfig || {}, this.pageConfig || {})
21
+ },
22
+ // 标题文本(兼容常见字段名)
23
+ titleText () {
24
+ return this.cfg.navigationBarTitleText || this.cfg.title || ''
25
+ },
26
+ // 背景色(兼容常见字段)
27
+ backgroundColor () {
28
+ return this.cfg.navigationBarBackgroundColor || '#ffffff'
29
+ },
30
+ // 文本颜色,微信小程序中 navigationBarTextStyle 为 white 或 black
31
+ textColor () {
32
+ const style = this.cfg.navigationBarTextStyle || 'black'
33
+ return style === 'white' ? '#ffffff' : '#000000'
34
+ },
35
+ // navigationStyle: 'default' | 'custom',custom 表示需要自定义绘制
36
+ navigationStyle () {
37
+ return this.cfg.navigationStyle || 'default'
38
+ },
39
+ // 是否隐藏(navigationStyle 为 'custom' 时也应隐藏)
40
+ hidden () {
41
+ return mpx.config?.webConfig?.enableTitleBar !== true || this.navigationStyle === 'custom'
42
+ },
43
+ // 是否展示返回按钮:根据浏览器历史判断(不依赖额外 page 配置)
44
+ showBack () {
45
+ console.log('showBack', this.$router.stack.length)
46
+ try {
47
+ return this.$router.stack.length > 1
48
+ } catch (e) {
49
+ return false
50
+ }
51
+ },
52
+ // safe area 顶部 padding,使用 env(safe-area-inset-top)
53
+ safeStyle () {
54
+ // 多数浏览器支持 env(), 为兼容也使用 constant() 备选(旧 iOS Safari)
55
+ return {
56
+ paddingTop: 'env(safe-area-inset-top, 0px)'
57
+ }
58
+ },
59
+ // 内部标题栏高度(遵循小程序常见平台差异)
60
+ innerHeight () {
61
+ const isIOS = /iP(hone|od|ad)/.test(navigator.userAgent)
62
+ return (isIOS ? 44 : 48) + 'px'
63
+ },
64
+ rootStyle () {
65
+ return {
66
+ background: this.backgroundColor,
67
+ color: this.textColor
68
+ }
69
+ },
70
+ innerStyle () {
71
+ return {
72
+ height: this.innerHeight
73
+ }
74
+ }
75
+ ,
76
+ // content wrapper style: padding-top to avoid being covered by fixed titlebar
77
+ contentStyle () {
78
+ // use calc to combine innerHeight and safe-area inset
79
+ return {
80
+ paddingTop: this.hidden ? '0px' : `calc(${this.innerHeight} + env(safe-area-inset-top, 0px))`,
81
+ // create its own layer to avoid overlapping issues
82
+ transform: 'translateZ(0)',
83
+ willChange: 'transform'
84
+ }
85
+ }
86
+ },
87
+ methods: {
88
+ // 左侧点击:派发事件并在可回退时回退
89
+ onLeftClick (e) {
90
+ this.$emit('click-left', e)
91
+ if (this.showBack) {
92
+ try { window.history.back() } catch (err) {}
93
+ }
94
+ }
95
+ },
96
+ render (h) {
97
+ const leftChildren = []
98
+
99
+ // default back button (SVG) — no left slot support
100
+ if (this.showBack) {
101
+ leftChildren.push(
102
+ h('button', {
103
+ class: 'mpx-titlebar__back',
104
+ attrs: { 'aria-label': 'back', type: 'button' }
105
+ }, [
106
+ h('svg', {
107
+ attrs: {
108
+ viewBox: '0 0 24 24',
109
+ width: '20',
110
+ height: '20',
111
+ fill: 'none',
112
+ xmlns: 'http://www.w3.org/2000/svg',
113
+ focusable: 'false',
114
+ 'aria-hidden': 'true'
115
+ }
116
+ }, [
117
+ h('path', {
118
+ attrs: {
119
+ d: 'M15 18l-6-6 6-6',
120
+ stroke: 'currentColor',
121
+ 'stroke-width': '2',
122
+ 'stroke-linecap': 'round',
123
+ 'stroke-linejoin': 'round'
124
+ }
125
+ })
126
+ ])
127
+ ])
128
+ )
129
+ }
130
+
131
+ // center shows title; only default slot (page content) is supported
132
+ const centerChildren = [
133
+ h('div', { class: 'mpx-titlebar__title', style: { color: this.textColor } }, [this.titleText])
134
+ ]
135
+
136
+ // top-level wrapper: contains fixed titlebar and page content wrapper
137
+ return h('page', { class: 'mpx-titlebar-wrapper' }, [
138
+ // fixed titlebar
139
+ h('div', {
140
+ class: ['mpx-titlebar', { 'mpx-titlebar--hidden': this.hidden }],
141
+ style: this.rootStyle
142
+ }, [
143
+ h('div', { class: 'mpx-titlebar__safe', style: this.safeStyle }, [
144
+ h('div', { class: 'mpx-titlebar__inner', style: this.innerStyle }, [
145
+ h('div', { class: 'mpx-titlebar__left', on: { click: this.onLeftClick } }, leftChildren),
146
+ h('div', { class: 'mpx-titlebar__center' }, centerChildren),
147
+ h('div', { class: 'mpx-titlebar__right' }, [])
148
+ ])
149
+ ])
150
+ ]),
151
+
152
+ // page content wrapper: default slot is page content
153
+ h('div', { class: 'mpx-titlebar__content', style: this.contentStyle }, this.$slots.default || [])
154
+ ])
155
+ }
156
+ }
157
+ </script>
158
+
159
+ <style scoped>
160
+ .mpx-titlebar {
161
+ width: 100%;
162
+ box-sizing: border-box;
163
+ -webkit-font-smoothing: antialiased;
164
+ }
165
+ .mpx-titlebar--hidden {
166
+ display: none;
167
+ }
168
+ .mpx-titlebar__safe {
169
+ /* safe area handled by padding-top; include both env and constant for broader iOS support */
170
+ padding-top: env(safe-area-inset-top, 0px);
171
+ padding-top: constant(safe-area-inset-top, 0px);
172
+ }
173
+ .mpx-titlebar__inner {
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: space-between;
177
+ padding: 0 12px;
178
+ box-sizing: border-box;
179
+ }
180
+ .mpx-titlebar__left,
181
+ .mpx-titlebar__right {
182
+ flex: 0 0 auto;
183
+ min-width: 44px;
184
+ display: flex;
185
+ align-items: center;
186
+ }
187
+ .mpx-titlebar__center {
188
+ flex: 1 1 auto;
189
+ display: flex;
190
+ align-items: center;
191
+ justify-content: center;
192
+ overflow: hidden;
193
+ padding: 0 8px;
194
+ }
195
+ .mpx-titlebar__title {
196
+ font-size: 17px;
197
+ white-space: nowrap;
198
+ text-overflow: ellipsis;
199
+ overflow: hidden;
200
+ font-weight: 500;
201
+ }
202
+ .mpx-titlebar__back {
203
+ background: none;
204
+ border: none;
205
+ font-size: 20px;
206
+ color: inherit;
207
+ padding: 6px;
208
+ cursor: pointer;
209
+ }
210
+
211
+ /* wrapper and content layout */
212
+ .mpx-titlebar-wrapper {
213
+ position: relative;
214
+ width: 100%;
215
+ height: 100%;
216
+ }
217
+
218
+ .mpx-titlebar {
219
+ position: fixed;
220
+ top: 0;
221
+ left: 0;
222
+ right: 0;
223
+ z-index: 1000; /* ensure above page content */
224
+ }
225
+
226
+ .mpx-titlebar__content {
227
+ position: relative;
228
+ width: 100%;
229
+ min-height: 100%;
230
+ box-sizing: border-box;
231
+ background: transparent;
232
+ }
233
+
234
+ /* SVG icon sizing and inherit color */
235
+ .mpx-titlebar__back svg {
236
+ display: block;
237
+ width: 20px;
238
+ height: 20px;
239
+ }
240
+ .mpx-titlebar__back path {
241
+ stroke: currentColor;
242
+ }
243
+ </style>
@@ -2476,7 +2476,14 @@ function getVirtualHostRoot (options, meta) {
2476
2476
  }
2477
2477
  }
2478
2478
  if (isWeb(mode) && ctorType === 'page') {
2479
- return createASTElement('page')
2479
+ const rootView = createASTElement('mpx-titlebar', [
2480
+ {
2481
+ name: 'pageConfig',
2482
+ value: '{{ this.$options.__mpxPageConfig }}'
2483
+ }
2484
+ ])
2485
+ processElement(rootView, rootView, options, meta)
2486
+ return rootView
2480
2487
  }
2481
2488
  }
2482
2489
  return getTempNode()
@@ -60,7 +60,9 @@ Vue.use(VueRouter)\n`
60
60
  globalTabBar
61
61
  })
62
62
 
63
- output += `var App = require(${stringifyRequest(loaderContext, addQuery(loaderContext.resource, { isApp: true }))}).default\n`
63
+ output += `var App = require(${stringifyRequest(loaderContext, addQuery(loaderContext.resource, { isApp: true }))}).default;\n`
64
+
65
+ output += `Vue.component('mpx-titlebar', require(${stringifyRequest(loaderContext, normalize.lib('runtime/components/web/mpx-titlebar.vue'))}).default);\n`
64
66
 
65
67
  output += `
66
68
  export default processAppOption({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpxjs/webpack-plugin",
3
- "version": "2.10.3-beta.7",
3
+ "version": "2.10.3-beta.9",
4
4
  "description": "mpx compile core",
5
5
  "keywords": [
6
6
  "mpx"