Compare commits
364 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0dd0cab15d | ||
![]() |
02d8fb1268 | ||
![]() |
ee8fc19a47 | ||
![]() |
dd11e45d5f | ||
![]() |
8455f09e8c | ||
![]() |
edefc99702 | ||
![]() |
946ad7c976 | ||
![]() |
cfe5634c91 | ||
![]() |
2000eda414 | ||
![]() |
69b7429909 | ||
![]() |
926d700b60 | ||
![]() |
f0cd8ee943 | ||
![]() |
67f76415e1 | ||
![]() |
e67da2203c | ||
![]() |
cb033104ee | ||
![]() |
28e59cd599 | ||
![]() |
1af7213e96 | ||
![]() |
d5c14ac3ae | ||
![]() |
27d4441ca6 | ||
![]() |
ea50a639ed | ||
![]() |
e171c29d28 | ||
![]() |
ae515cd85e | ||
![]() |
e1654359fd | ||
![]() |
32cf872405 | ||
![]() |
c355f6e8f1 | ||
![]() |
227f19a93c | ||
![]() |
9cf0c212bf | ||
![]() |
79c333e647 | ||
![]() |
9c074652e1 | ||
![]() |
6402f8825f | ||
![]() |
4fc50a6bb5 | ||
![]() |
28d8000ad8 | ||
![]() |
144dec8881 | ||
![]() |
e2c319079a | ||
![]() |
1efb68cb9b | ||
![]() |
ba7252c332 | ||
![]() |
548b0515f6 | ||
![]() |
b03f7ee310 | ||
![]() |
e2eda79403 | ||
![]() |
2a97bf0aaa | ||
![]() |
f8641457ad | ||
![]() |
407bc849ec | ||
![]() |
38328280c4 | ||
![]() |
d2f7b5e1ec | ||
![]() |
7b3e757301 | ||
![]() |
2a71d02ab8 | ||
![]() |
4b77ca6c13 | ||
![]() |
21234cdbe2 | ||
![]() |
5250fac0a1 | ||
![]() |
91faa0f301 | ||
![]() |
13ebc5aacc | ||
![]() |
d86bfa719d | ||
![]() |
72c261ef67 | ||
![]() |
a7671ce4a9 | ||
![]() |
de32f4515f | ||
![]() |
74e52f000a | ||
![]() |
7bd338e07c | ||
![]() |
119edac05a | ||
![]() |
f93ed08cb2 | ||
![]() |
393c37bd56 | ||
![]() |
7a1ae5b0f3 | ||
![]() |
c47564e51a | ||
![]() |
7a2e870641 | ||
![]() |
3b7688a20b | ||
![]() |
e2c0ac4721 | ||
![]() |
11c6f158d0 | ||
![]() |
9ef3b7d7d4 | ||
![]() |
4f67168494 | ||
![]() |
bf61c3d1f0 | ||
![]() |
9a34868fb7 | ||
![]() |
fe751745e5 | ||
![]() |
29f526242d | ||
![]() |
ba8c0a2e7f | ||
![]() |
33638c5a9e | ||
![]() |
6a7ece9b3f | ||
![]() |
47d8401c47 | ||
![]() |
11b7880810 | ||
![]() |
c1b12eab84 | ||
![]() |
136fb5d507 | ||
![]() |
4445e18b02 | ||
![]() |
7dd210e427 | ||
![]() |
2f2a14cd06 | ||
![]() |
3eb06627c2 | ||
![]() |
c6d4a46968 | ||
![]() |
09e048ba85 | ||
![]() |
968a090876 | ||
![]() |
39f4ba793d | ||
![]() |
ea2f3a6990 | ||
![]() |
f6208a191d | ||
![]() |
23cd9a522e | ||
![]() |
f2119f3f73 | ||
![]() |
61a2fd8a27 | ||
![]() |
3808f515cf | ||
![]() |
61bb8363cd | ||
![]() |
bd64215f86 | ||
![]() |
a4d366c01e | ||
![]() |
8f711caada | ||
![]() |
3a2b4febd2 | ||
![]() |
da79ac04e6 | ||
![]() |
95dbb93256 | ||
![]() |
6996037cd4 | ||
![]() |
cc886bd326 | ||
![]() |
c1200eabe8 | ||
![]() |
0864bc9e3f | ||
![]() |
26d49ac5bc | ||
![]() |
907c20d9d8 | ||
![]() |
25fb9705cc | ||
![]() |
fa18cfa406 | ||
![]() |
4efb14cbff | ||
![]() |
af1e9d727f | ||
![]() |
a79e59cdf3 | ||
![]() |
24622137c8 | ||
![]() |
fc7d30c510 | ||
![]() |
305ac6dccd | ||
![]() |
0723fb178b | ||
![]() |
2fb1e6d71c | ||
![]() |
6866c17184 | ||
![]() |
3893aff68f | ||
![]() |
1558430435 | ||
![]() |
02b78976bd | ||
![]() |
6da7d46913 | ||
![]() |
2e03861c55 | ||
![]() |
5f97a3ff8a | ||
![]() |
860e2c5e07 | ||
![]() |
12b9384df6 | ||
![]() |
a82f5d4624 | ||
![]() |
1f3749d08d | ||
![]() |
218d618e0e | ||
![]() |
4bed7f2d13 | ||
![]() |
2c566253f3 | ||
![]() |
a18880675b | ||
![]() |
28fad4a480 | ||
![]() |
008a1cc58a | ||
![]() |
b5a71b7788 | ||
![]() |
4e7dd994b4 | ||
![]() |
8233556683 | ||
![]() |
6362129e34 | ||
![]() |
8464a040ab | ||
![]() |
c99a11a9fa | ||
![]() |
41bd05fd0f | ||
![]() |
0d42825314 | ||
![]() |
e5db0193a3 | ||
![]() |
2b0d381af4 | ||
![]() |
8a434fa761 | ||
![]() |
aca9b3337d | ||
![]() |
c820ebf2d0 | ||
![]() |
b286d07623 | ||
![]() |
6eccf0a294 | ||
![]() |
da5a2b1afe | ||
![]() |
9dcea8189e | ||
![]() |
b8c6beabb5 | ||
![]() |
7e81d97742 | ||
![]() |
dcae738e69 | ||
![]() |
d5a3772b3a | ||
![]() |
f23d8128f9 | ||
![]() |
3bf7fcb844 | ||
![]() |
0b4147a15e | ||
![]() |
7b61242611 | ||
![]() |
13c166139d | ||
![]() |
51d399f38b | ||
![]() |
0f9431cf1e | ||
![]() |
e79ae9a17f | ||
![]() |
6bc115cd36 | ||
![]() |
3f08e45e06 | ||
![]() |
ad522ee570 | ||
![]() |
a0a2106fe7 | ||
![]() |
a5a359528a | ||
![]() |
901c677946 | ||
![]() |
0fbe59fe92 | ||
![]() |
c5a1b1cd9c | ||
![]() |
291be04925 | ||
![]() |
604dab5f38 | ||
![]() |
d6a883c23f | ||
![]() |
81a1e8e42d | ||
![]() |
91ee84cd49 | ||
![]() |
3e604e3c7f | ||
![]() |
9ad46fe088 | ||
![]() |
bb0a57bd1f | ||
![]() |
35bdf827d2 | ||
![]() |
720c7c7e35 | ||
![]() |
22ce0f481d | ||
![]() |
de55a88d0e | ||
![]() |
d76a5baad9 | ||
![]() |
667e0647c4 | ||
![]() |
a5121bfef6 | ||
![]() |
0042afe215 | ||
![]() |
b857669c0c | ||
![]() |
f4fa5127d7 | ||
![]() |
b454473591 | ||
![]() |
834e2b4ce5 | ||
![]() |
91d66fda51 | ||
![]() |
ef81357272 | ||
![]() |
6777d20d41 | ||
![]() |
99ca3d430a | ||
![]() |
1905a3f8bb | ||
![]() |
b5a74cf371 | ||
![]() |
54105b9968 | ||
![]() |
7e108dc5db | ||
![]() |
d980dbb97a | ||
![]() |
9f832d6bb8 | ||
![]() |
c594222abd | ||
![]() |
cd182d3e20 | ||
![]() |
53d68c10a0 | ||
![]() |
8762df2392 | ||
![]() |
b5cd1319c0 | ||
![]() |
ac32d6a97b | ||
![]() |
b6fbf54c1c | ||
![]() |
501e86a034 | ||
![]() |
200bdd366d | ||
![]() |
d35fabd893 | ||
![]() |
6673c2e269 | ||
![]() |
ae3a460d59 | ||
![]() |
cc56472da6 | ||
![]() |
b4316befdd | ||
![]() |
dd177f81bb | ||
![]() |
ad22d9f95f | ||
![]() |
6691e739de | ||
![]() |
a0fd6d92a2 | ||
![]() |
34a6b4b229 | ||
![]() |
3bea809f19 | ||
![]() |
19b0d1b921 | ||
![]() |
c5630e0636 | ||
![]() |
a57d4c08b3 | ||
![]() |
ff848585ae | ||
![]() |
cb33ed7a00 | ||
![]() |
6d3c92b003 | ||
![]() |
a7795a9b98 | ||
![]() |
efe570c8c9 | ||
![]() |
eef1e69a9e | ||
![]() |
b48ef5eb29 | ||
![]() |
43a3232368 | ||
![]() |
0bb2d56aa3 | ||
![]() |
9bcb37cd4f | ||
![]() |
49fa8c4509 | ||
![]() |
ef7c1fd389 | ||
![]() |
1fcc2fb7d5 | ||
![]() |
14a5e83710 | ||
![]() |
07cdfa7d08 | ||
![]() |
fc062e6480 | ||
![]() |
3c398ab101 | ||
![]() |
da32e6da0c | ||
![]() |
4d51cc7881 | ||
![]() |
c004ec6bfe | ||
![]() |
aed4b934ed | ||
![]() |
261d46eac0 | ||
![]() |
c9c7b9e539 | ||
![]() |
228568ba6f | ||
![]() |
a733211d18 | ||
![]() |
ca9511c610 | ||
![]() |
176a391974 | ||
![]() |
55f8cced0a | ||
![]() |
ade7e6599d | ||
![]() |
33de1f7591 | ||
![]() |
56bfdfaf70 | ||
![]() |
2959974f86 | ||
![]() |
085a1d73a2 | ||
![]() |
7f80ded4c2 | ||
![]() |
639c2a0e75 | ||
![]() |
3c7701d21b | ||
![]() |
5952515ac7 | ||
![]() |
13c3e78cc6 | ||
![]() |
e5103ddb7a | ||
![]() |
ac2128bb76 | ||
![]() |
5b55e01089 | ||
![]() |
e57853fda0 | ||
![]() |
16c27345ed | ||
![]() |
c214a15bb7 | ||
![]() |
24b43088a9 | ||
![]() |
1ee3d06ed8 | ||
![]() |
5144ceabd1 | ||
![]() |
7eb4c23116 | ||
![]() |
53ebadf0a2 | ||
![]() |
8c1dda51ed | ||
![]() |
637ca983cf | ||
![]() |
1b61a262da | ||
![]() |
6fe2b888e4 | ||
![]() |
003e8685a4 | ||
![]() |
7eab089118 | ||
![]() |
cd130de1cf | ||
![]() |
988cf6aa53 | ||
![]() |
7afa74d381 | ||
![]() |
1bec786aaa | ||
![]() |
46ae55c73c | ||
![]() |
0865a33ad4 | ||
![]() |
f01b98693e | ||
![]() |
8b357ab8c5 | ||
![]() |
7f41078fa0 | ||
![]() |
e4571097c8 | ||
![]() |
dec635a59d | ||
![]() |
5823f89e5f | ||
![]() |
d20f359769 | ||
![]() |
18d52a8e48 | ||
![]() |
3b2395da26 | ||
![]() |
12aae659fe | ||
![]() |
45744d412e | ||
![]() |
4b7a28fe97 | ||
![]() |
c1ed654ff3 | ||
![]() |
bb30afe1b6 | ||
![]() |
4ebc32dd3b | ||
![]() |
b1bfd0d336 | ||
![]() |
208f2395a6 | ||
![]() |
a616867641 | ||
![]() |
bde0332183 | ||
![]() |
588c3a446f | ||
![]() |
a9d3eccad5 | ||
![]() |
01679809b8 | ||
![]() |
a4fd81d2a8 | ||
![]() |
7ab753ee8d | ||
![]() |
e20b84c8d5 | ||
![]() |
77020a13a0 | ||
![]() |
79bbc09850 | ||
![]() |
556f9052d1 | ||
![]() |
3e2cc7be81 | ||
![]() |
5871cb5fd8 | ||
![]() |
3a088be68b | ||
![]() |
eab3de02fd | ||
![]() |
7e26a71db8 | ||
![]() |
8bf7145752 | ||
![]() |
fd1102b425 | ||
![]() |
005d2406ab | ||
![]() |
fbd6343b2b | ||
![]() |
b11b17ec40 | ||
![]() |
9736931bfd | ||
![]() |
85c34bc922 | ||
![]() |
70801959ea | ||
![]() |
aa758845f6 | ||
![]() |
c6eccbfa19 | ||
![]() |
3ae09fc804 | ||
![]() |
695486e41f | ||
![]() |
7cf16d608c | ||
![]() |
0d948a0691 | ||
![]() |
50cb3419c2 | ||
![]() |
7271f3c618 | ||
![]() |
d6c6022a18 | ||
![]() |
84d4607b2a | ||
![]() |
4f6d271c28 | ||
![]() |
ee52ae132d | ||
![]() |
de8df540c2 | ||
![]() |
6d4bb21f52 | ||
![]() |
ffa5c68e66 | ||
![]() |
5eadda62de | ||
![]() |
041403f5d8 | ||
![]() |
137fa5846b | ||
![]() |
ffc37d9030 | ||
![]() |
7e3ab3ca19 | ||
![]() |
cdae57adce | ||
![]() |
39007ec045 | ||
![]() |
d4935d1186 | ||
![]() |
12acce4575 | ||
![]() |
3f15da98de | ||
![]() |
fb4a6e7bec | ||
![]() |
6cdb56bb12 | ||
![]() |
e7d0a685d4 | ||
![]() |
c3debbb636 | ||
![]() |
a7c3dbaea2 | ||
![]() |
c032fe5a1e | ||
![]() |
6a018d89be | ||
![]() |
cc716a1e0c | ||
![]() |
be35efd530 | ||
![]() |
37b52d1243 | ||
![]() |
4770817fdc | ||
![]() |
3a12eb35eb | ||
![]() |
01916970e8 | ||
![]() |
ee43df4fa6 |
@ -1,106 +1,3 @@
|
|||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const { execSync } = require('child_process')
|
|
||||||
|
|
||||||
const scopes = fs
|
|
||||||
.readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
|
|
||||||
.filter((dirent) => dirent.isDirectory())
|
|
||||||
.map((dirent) => dirent.name.replace(/s$/, ''))
|
|
||||||
|
|
||||||
// precomputed scope
|
|
||||||
const scopeComplete = execSync('git status --porcelain || true')
|
|
||||||
.toString()
|
|
||||||
.trim()
|
|
||||||
.split('\n')
|
|
||||||
.find((r) => ~r.indexOf('M src'))
|
|
||||||
?.replace(/(\/)/g, '%%')
|
|
||||||
?.match(/src%%((\w|-)*)/)?.[1]
|
|
||||||
?.replace(/s$/, '')
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
ignores: [(commit) => commit.includes('init')],
|
|
||||||
extends: ['@commitlint/config-conventional'],
|
extends: ['@commitlint/config-conventional'],
|
||||||
rules: {
|
|
||||||
'body-leading-blank': [2, 'always'],
|
|
||||||
'footer-leading-blank': [1, 'always'],
|
|
||||||
'header-max-length': [2, 'always', 108],
|
|
||||||
'subject-empty': [2, 'never'],
|
|
||||||
'type-empty': [2, 'never'],
|
|
||||||
'subject-case': [0],
|
|
||||||
'type-enum': [
|
|
||||||
2,
|
|
||||||
'always',
|
|
||||||
[
|
|
||||||
'feat',
|
|
||||||
'fix',
|
|
||||||
'perf',
|
|
||||||
'style',
|
|
||||||
'docs',
|
|
||||||
'test',
|
|
||||||
'refactor',
|
|
||||||
'build',
|
|
||||||
'ci',
|
|
||||||
'chore',
|
|
||||||
'revert',
|
|
||||||
'wip',
|
|
||||||
'workflow',
|
|
||||||
'types',
|
|
||||||
'release',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
prompt: {
|
|
||||||
/** @use `pnpm commit :f` */
|
|
||||||
alias: {
|
|
||||||
f: 'docs: fix typos',
|
|
||||||
r: 'docs: update README',
|
|
||||||
s: 'style: update code format',
|
|
||||||
b: 'build: bump dependencies',
|
|
||||||
c: 'chore: update config',
|
|
||||||
},
|
|
||||||
customScopesAlign: !scopeComplete ? 'top' : 'bottom',
|
|
||||||
defaultScope: scopeComplete,
|
|
||||||
scopes: [...scopes, 'mock'],
|
|
||||||
allowEmptyIssuePrefixs: false,
|
|
||||||
allowCustomIssuePrefixs: false,
|
|
||||||
|
|
||||||
// English
|
|
||||||
typesAppend: [
|
|
||||||
{ value: 'wip', name: 'wip: work in process' },
|
|
||||||
{ value: 'workflow', name: 'workflow: workflow improvements' },
|
|
||||||
{ value: 'types', name: 'types: type definition file changes' },
|
|
||||||
],
|
|
||||||
|
|
||||||
// 中英文对照版
|
|
||||||
// messages: {
|
|
||||||
// type: '选择你要提交的类型 :',
|
|
||||||
// scope: '选择一个提交范围 (可选):',
|
|
||||||
// customScope: '请输入自定义的提交范围 :',
|
|
||||||
// subject: '填写简短精炼的变更描述 :\n',
|
|
||||||
// body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
|
|
||||||
// breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
|
|
||||||
// footerPrefixsSelect: '选择关联issue前缀 (可选):',
|
|
||||||
// customFooterPrefixs: '输入自定义issue前缀 :',
|
|
||||||
// footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
|
|
||||||
// confirmCommit: '是否提交或修改commit ?',
|
|
||||||
// },
|
|
||||||
// types: [
|
|
||||||
// { value: 'feat', name: 'feat: 新增功能' },
|
|
||||||
// { value: 'fix', name: 'fix: 修复缺陷' },
|
|
||||||
// { value: 'docs', name: 'docs: 文档变更' },
|
|
||||||
// { value: 'style', name: 'style: 代码格式' },
|
|
||||||
// { value: 'refactor', name: 'refactor: 代码重构' },
|
|
||||||
// { value: 'perf', name: 'perf: 性能优化' },
|
|
||||||
// { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
|
|
||||||
// { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
|
|
||||||
// { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
|
|
||||||
// { value: 'revert', name: 'revert: 回滚 commit' },
|
|
||||||
// { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
|
|
||||||
// { value: 'wip', name: 'wip: 正在开发中' },
|
|
||||||
// { value: 'workflow', name: 'workflow: 工作流程改进' },
|
|
||||||
// { value: 'types', name: 'types: 类型定义文件修改' },
|
|
||||||
// ],
|
|
||||||
// emptyScopesAlias: 'empty: 不填写',
|
|
||||||
// customScopesAlias: 'custom: 自定义',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
src/uni_modules/
|
|
@ -1,101 +0,0 @@
|
|||||||
{
|
|
||||||
"globals": {
|
|
||||||
"Component": true,
|
|
||||||
"ComponentPublicInstance": true,
|
|
||||||
"ComputedRef": true,
|
|
||||||
"EffectScope": true,
|
|
||||||
"ExtractDefaultPropTypes": true,
|
|
||||||
"ExtractPropTypes": true,
|
|
||||||
"ExtractPublicPropTypes": true,
|
|
||||||
"InjectionKey": true,
|
|
||||||
"PropType": true,
|
|
||||||
"Ref": true,
|
|
||||||
"VNode": true,
|
|
||||||
"WritableComputedRef": true,
|
|
||||||
"computed": true,
|
|
||||||
"createApp": true,
|
|
||||||
"customRef": true,
|
|
||||||
"defineAsyncComponent": true,
|
|
||||||
"defineComponent": true,
|
|
||||||
"effectScope": true,
|
|
||||||
"getCurrentInstance": true,
|
|
||||||
"getCurrentScope": true,
|
|
||||||
"h": true,
|
|
||||||
"inject": true,
|
|
||||||
"isProxy": true,
|
|
||||||
"isReactive": true,
|
|
||||||
"isReadonly": true,
|
|
||||||
"isRef": true,
|
|
||||||
"markRaw": true,
|
|
||||||
"nextTick": true,
|
|
||||||
"onActivated": true,
|
|
||||||
"onAddToFavorites": true,
|
|
||||||
"onBackPress": true,
|
|
||||||
"onBeforeMount": true,
|
|
||||||
"onBeforeUnmount": true,
|
|
||||||
"onBeforeUpdate": true,
|
|
||||||
"onDeactivated": true,
|
|
||||||
"onError": true,
|
|
||||||
"onErrorCaptured": true,
|
|
||||||
"onHide": true,
|
|
||||||
"onLaunch": true,
|
|
||||||
"onLoad": true,
|
|
||||||
"onMounted": true,
|
|
||||||
"onNavigationBarButtonTap": true,
|
|
||||||
"onNavigationBarSearchInputChanged": true,
|
|
||||||
"onNavigationBarSearchInputClicked": true,
|
|
||||||
"onNavigationBarSearchInputConfirmed": true,
|
|
||||||
"onNavigationBarSearchInputFocusChanged": true,
|
|
||||||
"onPageNotFound": true,
|
|
||||||
"onPageScroll": true,
|
|
||||||
"onPullDownRefresh": true,
|
|
||||||
"onReachBottom": true,
|
|
||||||
"onReady": true,
|
|
||||||
"onRenderTracked": true,
|
|
||||||
"onRenderTriggered": true,
|
|
||||||
"onResize": true,
|
|
||||||
"onScopeDispose": true,
|
|
||||||
"onServerPrefetch": true,
|
|
||||||
"onShareAppMessage": true,
|
|
||||||
"onShareTimeline": true,
|
|
||||||
"onShow": true,
|
|
||||||
"onTabItemTap": true,
|
|
||||||
"onThemeChange": true,
|
|
||||||
"onUnhandledRejection": true,
|
|
||||||
"onUnload": true,
|
|
||||||
"onUnmounted": true,
|
|
||||||
"onUpdated": true,
|
|
||||||
"provide": true,
|
|
||||||
"reactive": true,
|
|
||||||
"readonly": true,
|
|
||||||
"ref": true,
|
|
||||||
"resolveComponent": true,
|
|
||||||
"shallowReactive": true,
|
|
||||||
"shallowReadonly": true,
|
|
||||||
"shallowRef": true,
|
|
||||||
"toRaw": true,
|
|
||||||
"toRef": true,
|
|
||||||
"toRefs": true,
|
|
||||||
"toValue": true,
|
|
||||||
"triggerRef": true,
|
|
||||||
"unref": true,
|
|
||||||
"useAttrs": true,
|
|
||||||
"useCssModule": true,
|
|
||||||
"useCssVars": true,
|
|
||||||
"useRequest": true,
|
|
||||||
"useSlots": true,
|
|
||||||
"useUpload": true,
|
|
||||||
"useUpload2": true,
|
|
||||||
"watch": true,
|
|
||||||
"watchEffect": true,
|
|
||||||
"watchPostEffect": true,
|
|
||||||
"watchSyncEffect": true,
|
|
||||||
"DirectiveBinding": true,
|
|
||||||
"MaybeRef": true,
|
|
||||||
"MaybeRefOrGetter": true,
|
|
||||||
"onWatcherCleanup": true,
|
|
||||||
"useId": true,
|
|
||||||
"useModel": true,
|
|
||||||
"useTemplateRef": true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es2021: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:vue/vue3-essential',
|
|
||||||
// eslint-plugin-import 插件, @see https://www.npmjs.com/package/eslint-plugin-import
|
|
||||||
'plugin:import/recommended',
|
|
||||||
// eslint-config-airbnb-base 插件 已经改用 eslint-config-standard 插件
|
|
||||||
'standard',
|
|
||||||
// 1. 接入 prettier 的规则
|
|
||||||
'prettier',
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
'./.eslintrc-auto-import.json',
|
|
||||||
],
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
files: ['.eslintrc.{js,cjs}'],
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'script',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 'latest',
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
sourceType: 'module',
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
'@typescript-eslint',
|
|
||||||
'vue',
|
|
||||||
// 2. 加入 prettier 的 eslint 插件
|
|
||||||
'prettier',
|
|
||||||
// eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
|
|
||||||
'import',
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
// 3. 注意要加上这一句,开启 prettier 自动修复的功能
|
|
||||||
'prettier/prettier': 'error',
|
|
||||||
// turn on errors for missing imports
|
|
||||||
'import/no-unresolved': 'off',
|
|
||||||
// 对后缀的检测,否则 import 一个ts文件也会报错,需要手动添加'.ts', 增加了下面的配置后就不用了
|
|
||||||
'import/extensions': [
|
|
||||||
'error',
|
|
||||||
'ignorePackages',
|
|
||||||
{ js: 'never', jsx: 'never', ts: 'never', tsx: 'never' },
|
|
||||||
],
|
|
||||||
// 只允许1个默认导出,关闭,否则不能随意export xxx
|
|
||||||
'import/prefer-default-export': ['off'],
|
|
||||||
'no-console': ['off'],
|
|
||||||
// 'no-unused-vars': ['off'],
|
|
||||||
// '@typescript-eslint/no-unused-vars': ['off'],
|
|
||||||
// 解决vite.config.ts报错问题
|
|
||||||
'import/no-extraneous-dependencies': 'off',
|
|
||||||
'no-plusplus': 'off',
|
|
||||||
'no-shadow': 'off',
|
|
||||||
'vue/multi-word-component-names': 'off',
|
|
||||||
'vue/no-unused-vars': 'off',
|
|
||||||
'@typescript-eslint/no-unused-vars': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
'no-underscore-dangle': 'off',
|
|
||||||
'no-use-before-define': 'off',
|
|
||||||
'no-undef': 'off',
|
|
||||||
'no-unused-vars': 'off',
|
|
||||||
'no-param-reassign': 'off',
|
|
||||||
// 避免 `eslint` 对于 `typescript` 函数重载的误报
|
|
||||||
'no-redeclare': 'off',
|
|
||||||
'@typescript-eslint/no-redeclare': 'error',
|
|
||||||
},
|
|
||||||
// eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
|
|
||||||
settings: {
|
|
||||||
'import/parsers': {
|
|
||||||
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
|
||||||
},
|
|
||||||
'import/resolver': {
|
|
||||||
typescript: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
globals: {
|
|
||||||
$t: true,
|
|
||||||
uni: true,
|
|
||||||
UniApp: true,
|
|
||||||
wx: true,
|
|
||||||
WechatMiniprogram: true,
|
|
||||||
getCurrentPages: true,
|
|
||||||
UniHelper: true,
|
|
||||||
Page: true,
|
|
||||||
App: true,
|
|
||||||
NodeJS: true,
|
|
||||||
},
|
|
||||||
}
|
|
31
.github/release.yml
vendored
Normal file
31
.github/release.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
categories:
|
||||||
|
- title: 🚀 新功能
|
||||||
|
labels: [feat, feature]
|
||||||
|
- title: 🛠️ 修复
|
||||||
|
labels: [fix, bugfix]
|
||||||
|
- title: 💅 样式
|
||||||
|
labels: [style]
|
||||||
|
- title: 📄 文档
|
||||||
|
labels: [docs]
|
||||||
|
- title: ⚡️ 性能
|
||||||
|
labels: [perf]
|
||||||
|
- title: 🧪 测试
|
||||||
|
labels: [test]
|
||||||
|
- title: ♻️ 重构
|
||||||
|
labels: [refactor]
|
||||||
|
- title: 📦 构建
|
||||||
|
labels: [build]
|
||||||
|
- title: 🚨 补丁
|
||||||
|
labels: [patch, hotfix]
|
||||||
|
- title: 🌐 发布
|
||||||
|
labels: [release, publish]
|
||||||
|
- title: 🔧 流程
|
||||||
|
labels: [ci, cd, workflow]
|
||||||
|
- title: ⚙️ 配置
|
||||||
|
labels: [config, chore]
|
||||||
|
- title: 📁 文件
|
||||||
|
labels: [file]
|
||||||
|
- title: 🎨 格式化
|
||||||
|
labels: [format]
|
||||||
|
- title: 🔀 其他
|
||||||
|
labels: [other, misc]
|
81
.github/workflows/auto-merge.yml
vendored
81
.github/workflows/auto-merge.yml
vendored
@ -1,49 +1,80 @@
|
|||||||
name: Auto Merge Base to Other Branches
|
name: Auto Merge Main to Other Branches
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- base
|
- main
|
||||||
workflow_dispatch: # 手动触发
|
workflow_dispatch: # 手动触发
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
auto-merge:
|
merge-to-i18n:
|
||||||
|
name: Merge main into i18n
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
|
token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
|
||||||
|
|
||||||
- name: Merge base into main
|
- name: Merge main into i18n
|
||||||
run: |
|
|
||||||
git config user.name "GitHub Actions"
|
|
||||||
git config user.email "actions@github.com"
|
|
||||||
git checkout main
|
|
||||||
git merge base --no-ff -m "Auto merge base into main"
|
|
||||||
git push origin main
|
|
||||||
|
|
||||||
- name: Merge base into i18n
|
|
||||||
run: |
|
run: |
|
||||||
git config user.name "GitHub Actions"
|
git config user.name "GitHub Actions"
|
||||||
git config user.email "actions@github.com"
|
git config user.email "actions@github.com"
|
||||||
git checkout i18n
|
git checkout i18n
|
||||||
git merge base --no-ff -m "Auto merge base into i18n"
|
git merge main --no-ff -m "Auto merge main into i18n"
|
||||||
git push origin i18n
|
git push origin i18n
|
||||||
|
|
||||||
- name: Merge base into tabbar
|
merge-to-base-sard-ui:
|
||||||
run: |
|
name: Merge main into base-sard-ui
|
||||||
git config user.name "GitHub Actions"
|
runs-on: ubuntu-latest
|
||||||
git config user.email "actions@github.com"
|
steps:
|
||||||
git checkout tabbar
|
- name: Checkout repository
|
||||||
git merge base --no-ff -m "Auto merge base into tabbar"
|
uses: actions/checkout@v4
|
||||||
git push origin tabbar
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
|
||||||
|
|
||||||
- name: Merge base into spa
|
- name: Merge main into base-sard-ui
|
||||||
run: |
|
run: |
|
||||||
git config user.name "GitHub Actions"
|
git config user.name "GitHub Actions"
|
||||||
git config user.email "actions@github.com"
|
git config user.email "actions@github.com"
|
||||||
git checkout spa
|
git checkout base-sard-ui
|
||||||
git merge base --no-ff -m "Auto merge base into spa"
|
git merge main --no-ff -m "Auto merge main into base-sard-ui"
|
||||||
git push origin spa
|
git push origin base-sard-ui
|
||||||
|
|
||||||
|
merge-to-base-uv-ui:
|
||||||
|
name: Merge main into base-uv-ui
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
|
||||||
|
|
||||||
|
- name: Merge main into base-uv-ui
|
||||||
|
run: |
|
||||||
|
git config user.name "GitHub Actions"
|
||||||
|
git config user.email "actions@github.com"
|
||||||
|
git checkout base-uv-ui
|
||||||
|
git merge main --no-ff -m "Auto merge main into base-uv-ui"
|
||||||
|
git push origin base-uv-ui
|
||||||
|
|
||||||
|
merge-to-base-uview-plus:
|
||||||
|
name: Merge main into base-uview-plus
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
|
||||||
|
|
||||||
|
- name: Merge main into base-uview-plus
|
||||||
|
run: |
|
||||||
|
git config user.name "GitHub Actions"
|
||||||
|
git config user.email "actions@github.com"
|
||||||
|
git checkout base-uview-plus
|
||||||
|
git merge main --no-ff -m "Auto merge main into base-uview-plus"
|
||||||
|
git push origin base-uview-plus
|
||||||
|
119
.github/workflows/release-log.yml
vendored
Normal file
119
.github/workflows/release-log.yml
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
name: Auto Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: read
|
||||||
|
issues: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install yq
|
||||||
|
run: sudo snap install yq
|
||||||
|
|
||||||
|
- name: Generate changelog
|
||||||
|
id: changelog
|
||||||
|
env:
|
||||||
|
CONFIG_FILE: .github/release.yml
|
||||||
|
run: |
|
||||||
|
# 解析配置文件
|
||||||
|
declare -A category_map
|
||||||
|
while IFS=";" read -r title labels; do
|
||||||
|
for label in $labels; do
|
||||||
|
category_map[$label]="$title"
|
||||||
|
done
|
||||||
|
done < <(yq -o=tsv '.categories[] | [.title, (.labels | join(" "))] | join(";")' $CONFIG_FILE)
|
||||||
|
# 获取版本范围
|
||||||
|
mapfile -t tags < <(git tag -l --sort=-version:refname)
|
||||||
|
current_tag=${tags[0]}
|
||||||
|
previous_tag=${tags[1]:-}
|
||||||
|
if [[ -z "$previous_tag" ]]; then
|
||||||
|
commit_range="$current_tag"
|
||||||
|
echo "首次发布版本: $current_tag"
|
||||||
|
else
|
||||||
|
commit_range="$previous_tag..$current_tag"
|
||||||
|
echo "版本范围: $commit_range"
|
||||||
|
fi
|
||||||
|
# 获取所有符合规范的提交
|
||||||
|
commits=$(git log --pretty=format:"%s|%h" "$commit_range")
|
||||||
|
# 生成分类日志
|
||||||
|
declare -A log_entries
|
||||||
|
while IFS="|" read -r subject hash; do
|
||||||
|
# type=$(echo "$subject" | cut -d':' -f1 | tr -d ' ')
|
||||||
|
type=$(echo "$subject" | sed -E 's/^([[:alnum:]]+)(\(.*\))?:.*/\1/' | tr -d ' ')
|
||||||
|
found=0
|
||||||
|
for label in "${!category_map[@]}"; do
|
||||||
|
if [[ "$type" == "$label" ]]; then
|
||||||
|
entry="- ${subject} (${hash:0:7})"
|
||||||
|
log_entries[${category_map[$label]}]+="$entry"$'\n'
|
||||||
|
found=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ $found -eq 0 ]]; then
|
||||||
|
entry="- ${subject} (${hash:0:7})"
|
||||||
|
log_entries["其他"]+="$entry"$'\n'
|
||||||
|
fi
|
||||||
|
done <<< "$commits"
|
||||||
|
|
||||||
|
# 统计提交数量
|
||||||
|
commit_count=$(git log --oneline "$commit_range" | wc -l)
|
||||||
|
# 统计受影响的文件数量
|
||||||
|
file_count=$(git diff --name-only "$commit_range" | wc -l)
|
||||||
|
# 统计贡献者信息
|
||||||
|
contributor_stats=$(git shortlog -sn "$commit_range")
|
||||||
|
contributor_notes=""
|
||||||
|
while IFS= read -r line; do
|
||||||
|
commits=$(echo "$line" | awk '{print $1}')
|
||||||
|
name=$(echo "$line" | awk '{$1=""; print $0}' | sed 's/^ //')
|
||||||
|
contributor_notes+="- @${name} (${commits} commits)\n"
|
||||||
|
done <<< "$contributor_stats"
|
||||||
|
# 构建输出内容
|
||||||
|
release_notes="## 版本更新日志 ($current_tag)\n\n"
|
||||||
|
while IFS= read -r category; do
|
||||||
|
if [[ -n "${log_entries[$category]}" ]]; then
|
||||||
|
release_notes+="### $category\n${log_entries[$category]}\n"
|
||||||
|
fi
|
||||||
|
done < <(yq '.categories[].title' $CONFIG_FILE)
|
||||||
|
# 构建输出内容
|
||||||
|
release_notes="## 版本更新日志 ($current_tag)\n\n"
|
||||||
|
current_date=$(date +"%Y-%m-%d")
|
||||||
|
# 添加发布日期和下载统计信息
|
||||||
|
release_notes+=" ### 📅 发布日期: ${current_date}\n"
|
||||||
|
while IFS= read -r category; do
|
||||||
|
if [[ -n "${log_entries[$category]}" ]]; then
|
||||||
|
release_notes+="### $category\n${log_entries[$category]}\n"
|
||||||
|
fi
|
||||||
|
done < <(yq '.categories[].title' $CONFIG_FILE)
|
||||||
|
|
||||||
|
# 添加统计信息
|
||||||
|
release_notes+="### 📊 统计信息\n"
|
||||||
|
release_notes+="- 本次发布包含 ${commit_count} 个提交\n"
|
||||||
|
release_notes+="- 影响 ${file_count} 个文件\n\n"
|
||||||
|
# 添加贡献者信息
|
||||||
|
release_notes+="### 👥 贡献者\n"
|
||||||
|
release_notes+="感谢这些优秀的贡献者(按提交次数排序):\n"
|
||||||
|
release_notes+="${contributor_notes}\n"
|
||||||
|
release_notes+="---\n"
|
||||||
|
# 写入文件
|
||||||
|
echo -e "$release_notes" > changelog.md
|
||||||
|
echo "生成日志内容:"
|
||||||
|
cat changelog.md
|
||||||
|
- name: Create Release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
generateReleaseNotes: false
|
||||||
|
bodyFile: changelog.md
|
||||||
|
tag: ${{ github.ref_name }}
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -27,6 +27,8 @@ dist
|
|||||||
docs/.vitepress/dist
|
docs/.vitepress/dist
|
||||||
docs/.vitepress/cache
|
docs/.vitepress/cache
|
||||||
|
|
||||||
|
src/types
|
||||||
|
|
||||||
# lock 文件还是不要了,我主要的版本写死就好了
|
# lock 文件还是不要了,我主要的版本写死就好了
|
||||||
# pnpm-lock.yaml
|
# pnpm-lock.yaml
|
||||||
# package-lock.json
|
# package-lock.json
|
||||||
|
6
.husky/commit-msg
Executable file → Normal file
6
.husky/commit-msg
Executable file → Normal file
@ -1,5 +1 @@
|
|||||||
#!/usr/bin/env sh
|
npx --no-install commitlint --edit "$1"
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
# Run the commit-msg hook
|
|
||||||
npx --no-install commitlint --edit
|
|
6
.husky/pre-commit
Executable file → Normal file
6
.husky/pre-commit
Executable file → Normal file
@ -1,5 +1 @@
|
|||||||
#!/usr/bin/env sh
|
npx lint-staged --allow-empty
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
# Run the pre-commit hook
|
|
||||||
npx --no-install -- lint-staged
|
|
@ -1,3 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
|
||||||
# unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
|
# unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
|
||||||
auto-import.d.ts
|
auto-import.d.ts
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ module.exports = {
|
|||||||
htmlWhitespaceSensitivity: 'ignore',
|
htmlWhitespaceSensitivity: 'ignore',
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: '*.json',
|
files: '*.{json,jsonc}',
|
||||||
options: {
|
options: {
|
||||||
trailingComma: 'none',
|
trailingComma: 'none',
|
||||||
},
|
},
|
||||||
|
@ -1 +0,0 @@
|
|||||||
src/uni_modules/
|
|
@ -1,58 +0,0 @@
|
|||||||
// .stylelintrc.cjs
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
extends: [
|
|
||||||
// stylelint-config-standard 替换成了更宽松的 stylelint-config-recommended
|
|
||||||
'stylelint-config-recommended',
|
|
||||||
// stylelint-config-standard-scss 替换成了更宽松的 stylelint-config-recommended-scss
|
|
||||||
'stylelint-config-recommended-scss',
|
|
||||||
'stylelint-config-recommended-vue/scss',
|
|
||||||
'stylelint-config-html/vue',
|
|
||||||
'stylelint-config-recess-order',
|
|
||||||
],
|
|
||||||
plugins: ['stylelint-prettier'],
|
|
||||||
overrides: [
|
|
||||||
// 扫描 .vue/html 文件中的<style>标签内的样式
|
|
||||||
{
|
|
||||||
files: ['**/*.{vue,html}'],
|
|
||||||
customSyntax: 'postcss-html',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.{css,scss}'],
|
|
||||||
customSyntax: 'postcss-scss',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
// 自定义规则
|
|
||||||
rules: {
|
|
||||||
'prettier/prettier': true,
|
|
||||||
// 允许 global 、export 、v-deep等伪类
|
|
||||||
'selector-pseudo-class-no-unknown': [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
ignorePseudoClasses: ['global', 'export', 'v-deep', 'deep'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'unit-no-unknown': [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
ignoreUnits: ['rpx'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
// 处理小程序page标签不认识的问题
|
|
||||||
'selector-type-no-unknown': [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
ignoreTypes: ['page'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'comment-empty-line-before': 'never', // never|always|always-multi-line|never-multi-line
|
|
||||||
'custom-property-empty-line-before': 'never',
|
|
||||||
'no-empty-source': null,
|
|
||||||
'comment-no-empty': null,
|
|
||||||
'no-duplicate-selectors': null,
|
|
||||||
'scss/comment-no-empty': null,
|
|
||||||
'selector-class-pattern': null,
|
|
||||||
'font-family-no-missing-generic-family-keyword': null,
|
|
||||||
},
|
|
||||||
}
|
|
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@ -13,6 +13,8 @@
|
|||||||
"uni-helper.uni-ui-snippets-vscode",
|
"uni-helper.uni-ui-snippets-vscode",
|
||||||
"uni-helper.uni-app-snippets-vscode",
|
"uni-helper.uni-app-snippets-vscode",
|
||||||
"mrmlnc.vscode-json5",
|
"mrmlnc.vscode-json5",
|
||||||
"streetsidesoftware.code-spell-checker"
|
"streetsidesoftware.code-spell-checker",
|
||||||
|
"foxundermoon.shell-format",
|
||||||
|
"christian-kohler.path-intellisense"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
79
.vscode/settings.json
vendored
79
.vscode/settings.json
vendored
@ -1,14 +1,7 @@
|
|||||||
{
|
{
|
||||||
// 默认格式化工具选择prettier
|
// 默认格式化工具选择prettier
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
// 保存的时候自动格式化
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
//开启自动修复
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll": "explicit",
|
|
||||||
"source.fixAll.eslint": "explicit",
|
|
||||||
"source.fixAll.stylelint": "explicit"
|
|
||||||
},
|
|
||||||
// 配置stylelint检查的文件类型范围
|
// 配置stylelint检查的文件类型范围
|
||||||
"stylelint.validate": ["css", "scss", "vue", "html"], // 与package.json的scripts对应
|
"stylelint.validate": ["css", "scss", "vue", "html"], // 与package.json的scripts对应
|
||||||
"stylelint.enable": true,
|
"stylelint.enable": true,
|
||||||
@ -41,12 +34,14 @@
|
|||||||
"commitlint",
|
"commitlint",
|
||||||
"dcloudio",
|
"dcloudio",
|
||||||
"iconfont",
|
"iconfont",
|
||||||
|
"oxlint",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"refresherrefresh",
|
"refresherrefresh",
|
||||||
"scrolltolower",
|
"scrolltolower",
|
||||||
"tabbar",
|
"tabbar",
|
||||||
"Toutiao",
|
"Toutiao",
|
||||||
"unibest",
|
"unibest",
|
||||||
|
"uview",
|
||||||
"uvui",
|
"uvui",
|
||||||
"Wechat",
|
"Wechat",
|
||||||
"WechatMiniprogram",
|
"WechatMiniprogram",
|
||||||
@ -56,7 +51,69 @@
|
|||||||
"explorer.fileNesting.enabled": true,
|
"explorer.fileNesting.enabled": true,
|
||||||
"explorer.fileNesting.expand": false,
|
"explorer.fileNesting.expand": false,
|
||||||
"explorer.fileNesting.patterns": {
|
"explorer.fileNesting.patterns": {
|
||||||
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,.npmrc,.browserslistrc",
|
"README.md": "index.html,favicon.ico,robots.txt,CHANGELOG.md",
|
||||||
".eslintrc.cjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,.stylelintrc.*,.eslintrc-auto-import.json,.editorconfig,.commitlint.cjs"
|
"pages.config.ts": "manifest.config.ts,openapi-ts-request.config.ts",
|
||||||
}
|
"package.json": "tsconfig.json,pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,.npmrc,.browserslistrc",
|
||||||
|
"eslint.config.mjs": ".commitlintrc.*,.prettier*,.editorconfig,.commitlint.cjs,.eslint*"
|
||||||
|
},
|
||||||
|
|
||||||
|
// // 保存的时候自动格式化
|
||||||
|
// "prettier.enable": true,
|
||||||
|
// "editor.formatOnSave": true,
|
||||||
|
// // 开启自动修复
|
||||||
|
// "editor.codeActionsOnSave": {
|
||||||
|
// "source.fixAll": "explicit",
|
||||||
|
// "source.fixAll.eslint": "explicit",
|
||||||
|
// "source.fixAll.stylelint": "explicit"
|
||||||
|
// },
|
||||||
|
|
||||||
|
// Disable the default formatter, use eslint instead
|
||||||
|
"prettier.enable": false,
|
||||||
|
"editor.formatOnSave": false,
|
||||||
|
|
||||||
|
// Auto fix
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": "explicit",
|
||||||
|
"source.organizeImports": "never"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||||
|
"eslint.rules.customizations": [
|
||||||
|
{ "rule": "style/*", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "format/*", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-indent", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-spacing", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-spaces", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-order", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-dangle", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-newline", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*quotes", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*semi", "severity": "off", "fixable": true }
|
||||||
|
],
|
||||||
|
|
||||||
|
// Enable eslint for all supported languages
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"typescriptreact",
|
||||||
|
"vue",
|
||||||
|
"html",
|
||||||
|
"markdown",
|
||||||
|
"json",
|
||||||
|
"json5",
|
||||||
|
"jsonc",
|
||||||
|
"yaml",
|
||||||
|
"toml",
|
||||||
|
"xml",
|
||||||
|
"gql",
|
||||||
|
"graphql",
|
||||||
|
"astro",
|
||||||
|
"svelte",
|
||||||
|
"css",
|
||||||
|
"less",
|
||||||
|
"scss",
|
||||||
|
"pcss",
|
||||||
|
"postcss"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
26
.vscode/vue3.code-snippets
vendored
26
.vscode/vue3.code-snippets
vendored
@ -27,12 +27,12 @@
|
|||||||
" },",
|
" },",
|
||||||
"}",
|
"}",
|
||||||
"</route>\n",
|
"</route>\n",
|
||||||
"<template>",
|
|
||||||
" <view class=\"\">$2</view>",
|
|
||||||
"</template>\n",
|
|
||||||
"<script lang=\"ts\" setup>",
|
"<script lang=\"ts\" setup>",
|
||||||
"//$3",
|
"//$3",
|
||||||
"</script>\n",
|
"</script>\n",
|
||||||
|
"<template>",
|
||||||
|
" <view class=\"\">$2</view>",
|
||||||
|
"</template>\n",
|
||||||
"<style lang=\"scss\" scoped>",
|
"<style lang=\"scss\" scoped>",
|
||||||
"//$4",
|
"//$4",
|
||||||
"</style>\n",
|
"</style>\n",
|
||||||
@ -41,16 +41,28 @@
|
|||||||
"Print unibest style": {
|
"Print unibest style": {
|
||||||
"scope": "vue",
|
"scope": "vue",
|
||||||
"prefix": "st",
|
"prefix": "st",
|
||||||
"body": ["<style lang=\"scss\" scoped>", "//", "</style>\n"],
|
"body": [
|
||||||
|
"<style lang=\"scss\" scoped>",
|
||||||
|
"//",
|
||||||
|
"</style>\n"
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Print unibest script": {
|
"Print unibest script": {
|
||||||
"scope": "vue",
|
"scope": "vue",
|
||||||
"prefix": "sc",
|
"prefix": "sc",
|
||||||
"body": ["<script lang=\"ts\" setup>", "//$3", "</script>\n"],
|
"body": [
|
||||||
|
"<script lang=\"ts\" setup>",
|
||||||
|
"//$3",
|
||||||
|
"</script>\n"
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"Print unibest template": {
|
"Print unibest template": {
|
||||||
"scope": "vue",
|
"scope": "vue",
|
||||||
"prefix": "te",
|
"prefix": "te",
|
||||||
"body": ["<template>", " <view class=\"\">$1</view>", "</template>\n"],
|
"body": [
|
||||||
|
"<template>",
|
||||||
|
" <view class=\"\">$1</view>",
|
||||||
|
"</template>\n"
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
11
README.md
11
README.md
@ -63,21 +63,20 @@
|
|||||||
## 📂 快速开始
|
## 📂 快速开始
|
||||||
|
|
||||||
执行 `pnpm create unibest` 创建项目
|
执行 `pnpm create unibest` 创建项目
|
||||||
|
|
||||||
执行 `pnpm i` 安装依赖
|
执行 `pnpm i` 安装依赖
|
||||||
|
|
||||||
执行 `pnpm dev` 运行 `H5`
|
执行 `pnpm dev` 运行 `H5`
|
||||||
|
执行 `pnpm dev:mp` 运行 `微信小程序`
|
||||||
|
|
||||||
## 📦 运行(支持热更新)
|
## 📦 运行(支持热更新)
|
||||||
|
|
||||||
- web平台: `pnpm dev:h5`, 然后打开 [http://localhost:9000/](http://localhost:9000/)。
|
- web平台: `pnpm dev:h5`, 然后打开 [http://localhost:9000/](http://localhost:9000/)。
|
||||||
- weixin平台:`pnpm dev:mp-weixin` 然后打开微信开发者工具,导入本地文件夹,选择本项目的`dist/dev/mp-weixin` 文件。
|
- weixin平台:`pnpm dev:mp` 然后打开微信开发者工具,导入本地文件夹,选择本项目的`dist/dev/mp-weixin` 文件。
|
||||||
- APP平台:`pnpm dev:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/dev/app` 文件夹,选择运行到模拟器(开发时优先使用),或者运行的安卓/ios基座。
|
- APP平台:`pnpm dev:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/dev/app` 文件夹,选择运行到模拟器(开发时优先使用),或者运行的安卓/ios基座。
|
||||||
|
|
||||||
## 🔗 发布
|
## 🔗 发布
|
||||||
|
|
||||||
- web平台: `pnpm build:h5`,打包后的文件在 `dist/build/h5`,可以放到web服务器,如nginx运行。如果最终不是放在根目录,可以在 `manifest.config.ts` 文件的 `h5.router.base` 属性进行修改。
|
- web平台: `pnpm build:h5`,打包后的文件在 `dist/build/h5`,可以放到web服务器,如nginx运行。如果最终不是放在根目录,可以在 `manifest.config.ts` 文件的 `h5.router.base` 属性进行修改。
|
||||||
- weixin平台:`pnpm build:mp-weixin`, 打包后的文件在 `dist/build/mp-weixin`,然后通过微信开发者工具导入,并点击右上角的“上传”按钮进行上传。
|
- weixin平台:`pnpm build:mp`, 打包后的文件在 `dist/build/mp-weixin`,然后通过微信开发者工具导入,并点击右上角的“上传”按钮进行上传。
|
||||||
- APP平台:`pnpm build:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/build/app` 文件夹,选择发行 - APP云打包。
|
- APP平台:`pnpm build:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/build/app` 文件夹,选择发行 - APP云打包。
|
||||||
|
|
||||||
## 📄 License
|
## 📄 License
|
||||||
@ -89,6 +88,6 @@ Copyright (c) 2025 菲鸽
|
|||||||
## 捐赠
|
## 捐赠
|
||||||
|
|
||||||
<p align='center'>
|
<p align='center'>
|
||||||
<img alt="special sponsor appwrite" src="./screenshots/pay-1.png" height="330" style="display:inline-block; height:330px;">
|
<img alt="special sponsor appwrite" src="https://oss.laf.run/ukw0y1-site/pay/wepay.png" height="330" style="display:inline-block; height:330px;">
|
||||||
<img alt="special sponsor appwrite" src="./screenshots/pay-2.png" height="330" style="display:inline-block; height:330px; margin-left:10px;">
|
<img alt="special sponsor appwrite" src="https://oss.laf.run/ukw0y1-site/pay/alipay.jpg" height="330" style="display:inline-block; height:330px; margin-left:10px;">
|
||||||
</p>
|
</p>
|
||||||
|
5
env/.env
vendored
5
env/.env
vendored
@ -1,12 +1,15 @@
|
|||||||
VITE_APP_TITLE = 'unibest'
|
VITE_APP_TITLE = 'unibest'
|
||||||
VITE_APP_PORT = 9000
|
VITE_APP_PORT = 9000
|
||||||
|
|
||||||
VITE_UNI_APPID = 'H57F2ACE4'
|
VITE_UNI_APPID = '__UNI__D1E5001'
|
||||||
VITE_WX_APPID = 'wxa2abb91f64032a2b'
|
VITE_WX_APPID = 'wxa2abb91f64032a2b'
|
||||||
|
|
||||||
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
|
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
|
||||||
VITE_APP_PUBLIC_BASE=/
|
VITE_APP_PUBLIC_BASE=/
|
||||||
|
|
||||||
|
# 登录页面
|
||||||
|
VITE_LOGIN_URL = '/pages/login/index'
|
||||||
|
|
||||||
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
|
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
|
||||||
VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
|
VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
|
||||||
|
|
||||||
|
22
eslint.config.mjs
Normal file
22
eslint.config.mjs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import uniHelper from '@uni-helper/eslint-config'
|
||||||
|
|
||||||
|
export default uniHelper({
|
||||||
|
unocss: true,
|
||||||
|
vue: true,
|
||||||
|
markdown: false,
|
||||||
|
ignores: [
|
||||||
|
'src/uni_modules/',
|
||||||
|
'dist',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'vue/no-unused-refs': 'off',
|
||||||
|
'unused-imports/no-unused-vars': 'off',
|
||||||
|
'eslint-comments/no-unlimited-disable': 'off',
|
||||||
|
'jsdoc/check-param-names': 'off',
|
||||||
|
'jsdoc/require-returns-description': 'off',
|
||||||
|
'ts/no-empty-object-type': 'off',
|
||||||
|
'no-extend-native': 'off',
|
||||||
|
},
|
||||||
|
})
|
@ -1,6 +1,7 @@
|
|||||||
|
import path from 'node:path'
|
||||||
|
import process from 'node:process'
|
||||||
// manifest.config.ts
|
// manifest.config.ts
|
||||||
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
|
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
|
||||||
import path from 'node:path'
|
|
||||||
import { loadEnv } from 'vite'
|
import { loadEnv } from 'vite'
|
||||||
|
|
||||||
// 获取环境变量的范例
|
// 获取环境变量的范例
|
||||||
@ -14,14 +15,14 @@ const {
|
|||||||
} = env
|
} = env
|
||||||
|
|
||||||
export default defineManifestConfig({
|
export default defineManifestConfig({
|
||||||
name: VITE_APP_TITLE,
|
'name': VITE_APP_TITLE,
|
||||||
appid: VITE_UNI_APPID,
|
'appid': VITE_UNI_APPID,
|
||||||
description: '',
|
'description': '',
|
||||||
versionName: '1.0.0',
|
'versionName': '1.0.0',
|
||||||
versionCode: '100',
|
'versionCode': '100',
|
||||||
transformPx: false,
|
'transformPx': false,
|
||||||
locale: VITE_FALLBACK_LOCALE, // 'zh-Hans'
|
'locale': VITE_FALLBACK_LOCALE, // 'zh-Hans'
|
||||||
h5: {
|
'h5': {
|
||||||
router: {
|
router: {
|
||||||
base: VITE_APP_PUBLIC_BASE,
|
base: VITE_APP_PUBLIC_BASE,
|
||||||
},
|
},
|
||||||
@ -82,14 +83,14 @@ export default defineManifestConfig({
|
|||||||
ios: {
|
ios: {
|
||||||
appstore: 'static/app/icons/1024x1024.png',
|
appstore: 'static/app/icons/1024x1024.png',
|
||||||
ipad: {
|
ipad: {
|
||||||
app: 'static/app/icons/76x76.png',
|
'app': 'static/app/icons/76x76.png',
|
||||||
'app@2x': 'static/app/icons/152x152.png',
|
'app@2x': 'static/app/icons/152x152.png',
|
||||||
notification: 'static/app/icons/20x20.png',
|
'notification': 'static/app/icons/20x20.png',
|
||||||
'notification@2x': 'static/app/icons/40x40.png',
|
'notification@2x': 'static/app/icons/40x40.png',
|
||||||
'proapp@2x': 'static/app/icons/167x167.png',
|
'proapp@2x': 'static/app/icons/167x167.png',
|
||||||
settings: 'static/app/icons/29x29.png',
|
'settings': 'static/app/icons/29x29.png',
|
||||||
'settings@2x': 'static/app/icons/58x58.png',
|
'settings@2x': 'static/app/icons/58x58.png',
|
||||||
spotlight: 'static/app/icons/40x40.png',
|
'spotlight': 'static/app/icons/40x40.png',
|
||||||
'spotlight@2x': 'static/app/icons/80x80.png',
|
'spotlight@2x': 'static/app/icons/80x80.png',
|
||||||
},
|
},
|
||||||
iphone: {
|
iphone: {
|
||||||
@ -107,12 +108,15 @@ export default defineManifestConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
/* 快应用特有相关 */
|
/* 快应用特有相关 */
|
||||||
quickapp: {},
|
'quickapp': {},
|
||||||
/* 小程序特有相关 */
|
/* 小程序特有相关 */
|
||||||
'mp-weixin': {
|
'mp-weixin': {
|
||||||
appid: VITE_WX_APPID,
|
appid: VITE_WX_APPID,
|
||||||
setting: {
|
setting: {
|
||||||
urlCheck: false,
|
urlCheck: false,
|
||||||
|
// 是否启用 ES6 转 ES5
|
||||||
|
es6: true,
|
||||||
|
minified: true,
|
||||||
},
|
},
|
||||||
usingComponents: true,
|
usingComponents: true,
|
||||||
// __usePrivacyCheck__: true,
|
// __usePrivacyCheck__: true,
|
||||||
@ -127,8 +131,8 @@ export default defineManifestConfig({
|
|||||||
'mp-toutiao': {
|
'mp-toutiao': {
|
||||||
usingComponents: true,
|
usingComponents: true,
|
||||||
},
|
},
|
||||||
uniStatistics: {
|
'uniStatistics': {
|
||||||
enable: false,
|
enable: false,
|
||||||
},
|
},
|
||||||
vueVersion: '3',
|
'vueVersion': '3',
|
||||||
})
|
})
|
||||||
|
135
package.json
135
package.json
@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "unibest",
|
"name": "unibest",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"version": "2.7.0",
|
"version": "3.1.0",
|
||||||
"description": "unibest - 最好的 uniapp 开发模板",
|
"description": "unibest - 最好的 uniapp 开发模板",
|
||||||
|
"update-time": "2025-06-21",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "feige996",
|
"name": "feige996",
|
||||||
"zhName": "菲鸽",
|
"zhName": "菲鸽",
|
||||||
@ -11,13 +12,14 @@
|
|||||||
"gitee": "https://gitee.com/feige996"
|
"gitee": "https://gitee.com/feige996"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"homepage": "https://unibest.tech",
|
||||||
"repository": "https://github.com/feige996/unibest",
|
"repository": "https://github.com/feige996/unibest",
|
||||||
"repository-gitee": "https://gitee.com/feige996/unibest",
|
"repository-gitee": "https://gitee.com/feige996/unibest",
|
||||||
"repository-deprecated": "https://github.com/codercup/unibest",
|
"repository-old": "https://github.com/codercup/unibest",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/feige996/unibest/issues"
|
"url": "https://github.com/feige996/unibest/issues",
|
||||||
|
"url-old": "https://github.com/codercup/unibest/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://feige996.github.io/unibest/",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18",
|
"node": ">=18",
|
||||||
"pnpm": ">=7.30"
|
"pnpm": ">=7.30"
|
||||||
@ -67,109 +69,90 @@
|
|||||||
"build:quickapp-webview": "uni build -p quickapp-webview",
|
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||||
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||||
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
|
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
|
||||||
"prepare": "git init && husky install",
|
|
||||||
"type-check": "vue-tsc --noEmit",
|
"type-check": "vue-tsc --noEmit",
|
||||||
"cz": "czg",
|
"openapi-ts-request": "openapi-ts",
|
||||||
"openapi-ts-request": "openapi-ts"
|
"prepare": "git init && husky",
|
||||||
},
|
"lint": "eslint",
|
||||||
"lint-staged": {
|
"lint:fix": "eslint --fix"
|
||||||
"**/*.{html,vue,ts,cjs,json,md}": [
|
|
||||||
"prettier --write"
|
|
||||||
],
|
|
||||||
"**/*.{vue,js,ts,jsx,tsx}": [
|
|
||||||
"eslint --cache --fix"
|
|
||||||
],
|
|
||||||
"**/*.{vue,css,scss,html}": [
|
|
||||||
"stylelint --fix"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"bin-wrapper": "npm:bin-wrapper-china"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dcloudio/uni-app": "3.0.0-4060520250512001",
|
"@dcloudio/uni-app": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-app-harmony": "3.0.0-4060520250512001",
|
"@dcloudio/uni-app-harmony": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-app-plus": "3.0.0-4060520250512001",
|
"@dcloudio/uni-app-plus": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-components": "3.0.0-4060520250512001",
|
"@dcloudio/uni-components": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-h5": "3.0.0-4060520250512001",
|
"@dcloudio/uni-h5": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-alipay": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-alipay": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-baidu": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-baidu": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-harmony": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-harmony": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-jd": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-jd": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-kuaishou": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-kuaishou": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-lark": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-lark": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-qq": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-qq": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-toutiao": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-toutiao": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-weixin": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-weixin": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-mp-xhs": "3.0.0-4060520250512001",
|
"@dcloudio/uni-mp-xhs": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-quickapp-webview": "3.0.0-4060520250512001",
|
"@dcloudio/uni-quickapp-webview": "3.0.0-4060620250520001",
|
||||||
"@tanstack/vue-query": "^5.62.16",
|
"@tanstack/vue-query": "^5.62.16",
|
||||||
"abortcontroller-polyfill": "^1.7.8",
|
"abortcontroller-polyfill": "^1.7.8",
|
||||||
"dayjs": "1.11.10",
|
"dayjs": "1.11.10",
|
||||||
|
"js-cookie": "^3.0.5",
|
||||||
"pinia": "2.0.36",
|
"pinia": "2.0.36",
|
||||||
"pinia-plugin-persistedstate": "3.2.1",
|
"pinia-plugin-persistedstate": "3.2.1",
|
||||||
"qs": "6.5.3",
|
"vue": "^3.4.21",
|
||||||
"vue": "3.4.21",
|
"wot-design-uni": "^1.9.1",
|
||||||
"vue-i18n": "^9.1.9",
|
"z-paging": "2.8.7"
|
||||||
"wot-design-uni": "^1.4.0",
|
|
||||||
"z-paging": "^2.8.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^18.6.1",
|
"@antfu/eslint-config": "^4.15.0",
|
||||||
"@commitlint/config-conventional": "^18.6.3",
|
"@commitlint/cli": "^19.8.1",
|
||||||
"@dcloudio/types": "^3.4.14",
|
"@commitlint/config-conventional": "^19.8.1",
|
||||||
"@dcloudio/uni-automator": "3.0.0-4060520250512001",
|
"@dcloudio/types": "^3.4.8",
|
||||||
"@dcloudio/uni-cli-shared": "3.0.0-4060520250512001",
|
"@dcloudio/uni-automator": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/uni-stacktracey": "3.0.0-4060520250512001",
|
"@dcloudio/uni-cli-shared": "3.0.0-4060620250520001",
|
||||||
"@dcloudio/vite-plugin-uni": "3.0.0-4060520250512001",
|
"@dcloudio/uni-stacktracey": "3.0.0-4060620250520001",
|
||||||
|
"@dcloudio/vite-plugin-uni": "3.0.0-4060620250520001",
|
||||||
"@esbuild/darwin-arm64": "0.20.2",
|
"@esbuild/darwin-arm64": "0.20.2",
|
||||||
"@esbuild/darwin-x64": "0.20.2",
|
"@esbuild/darwin-x64": "0.20.2",
|
||||||
"@iconify-json/carbon": "^1.2.4",
|
"@iconify-json/carbon": "^1.2.4",
|
||||||
"@rollup/rollup-darwin-x64": "^4.28.0",
|
"@rollup/rollup-darwin-x64": "^4.28.0",
|
||||||
"@types/node": "^20.17.9",
|
"@types/node": "^20.17.9",
|
||||||
"@types/wechat-miniprogram": "^3.4.8",
|
"@types/wechat-miniprogram": "^3.4.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@uni-helper/eslint-config": "^0.4.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
|
||||||
"@uni-helper/uni-types": "1.0.0-alpha.3",
|
"@uni-helper/uni-types": "1.0.0-alpha.3",
|
||||||
"@uni-helper/vite-plugin-uni-layouts": "^0.1.10",
|
"@uni-helper/unocss-preset-uni": "^0.2.11",
|
||||||
"@uni-helper/vite-plugin-uni-manifest": "^0.2.8",
|
"@uni-helper/vite-plugin-uni-components": "0.2.0",
|
||||||
"@uni-helper/vite-plugin-uni-pages": "0.2.20",
|
"@uni-helper/vite-plugin-uni-layouts": "0.1.10",
|
||||||
"@uni-helper/vite-plugin-uni-platform": "^0.0.4",
|
"@uni-helper/vite-plugin-uni-manifest": "0.2.8",
|
||||||
|
"@uni-helper/vite-plugin-uni-pages": "0.2.28",
|
||||||
|
"@uni-helper/vite-plugin-uni-platform": "0.0.4",
|
||||||
|
"@uni-ku/bundle-optimizer": "^1.3.3",
|
||||||
|
"@unocss/eslint-plugin": "^66.2.3",
|
||||||
"@unocss/preset-legacy-compat": "^0.59.4",
|
"@unocss/preset-legacy-compat": "^0.59.4",
|
||||||
"@vue/runtime-core": "^3.5.13",
|
"@vue/runtime-core": "^3.4.21",
|
||||||
"@vue/tsconfig": "^0.1.3",
|
"@vue/tsconfig": "^0.1.3",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"commitlint": "^18.6.1",
|
"eslint": "^9.29.0",
|
||||||
"czg": "^1.9.4",
|
"husky": "^9.1.7",
|
||||||
"eslint": "^8.57.1",
|
|
||||||
"eslint-config-prettier": "^9.1.0",
|
|
||||||
"eslint-config-standard": "^17.1.0",
|
|
||||||
"eslint-import-resolver-typescript": "^3.7.0",
|
|
||||||
"eslint-plugin-import": "^2.31.0",
|
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
|
||||||
"eslint-plugin-vue": "^9.32.0",
|
|
||||||
"husky": "^8.0.3",
|
|
||||||
"lint-staged": "^15.2.10",
|
"lint-staged": "^15.2.10",
|
||||||
"openapi-ts-request": "^1.1.2",
|
"openapi-ts-request": "^1.1.2",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.4.49",
|
||||||
"postcss-html": "^1.7.0",
|
"postcss-html": "^1.7.0",
|
||||||
"postcss-scss": "^4.0.9",
|
"postcss-scss": "^4.0.9",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"sass": "1.77.8",
|
"sass": "1.77.8",
|
||||||
"stylelint": "^16.11.0",
|
|
||||||
"stylelint-config-html": "^1.1.0",
|
|
||||||
"stylelint-config-recess-order": "^4.6.0",
|
|
||||||
"stylelint-config-recommended": "^14.0.1",
|
|
||||||
"stylelint-config-recommended-scss": "^14.1.0",
|
|
||||||
"stylelint-config-recommended-vue": "^1.5.0",
|
|
||||||
"stylelint-prettier": "^5.0.2",
|
|
||||||
"terser": "^5.36.0",
|
"terser": "^5.36.0",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"unocss": "^0.58.9",
|
"unocss": "65.4.2",
|
||||||
"unocss-applet": "^0.7.8",
|
|
||||||
"unplugin-auto-import": "^0.17.8",
|
"unplugin-auto-import": "^0.17.8",
|
||||||
"vite": "5.2.8",
|
"vite": "5.2.8",
|
||||||
"vite-plugin-restart": "^0.4.2",
|
"vite-plugin-restart": "^0.4.2",
|
||||||
"vue-tsc": "^1.8.27"
|
"vue-tsc": "^2.2.10"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"bin-wrapper": "npm:bin-wrapper-china"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*": "eslint --fix"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
|
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
|
||||||
|
import { tabBar } from './src/layouts/fg-tabbar/tabbarList'
|
||||||
|
|
||||||
export default defineUniPages({
|
export default defineUniPages({
|
||||||
globalStyle: {
|
globalStyle: {
|
||||||
@ -11,34 +12,12 @@ export default defineUniPages({
|
|||||||
easycom: {
|
easycom: {
|
||||||
autoscan: true,
|
autoscan: true,
|
||||||
custom: {
|
custom: {
|
||||||
|
'^fg-(.*)': '@/components/fg-$1/fg-$1.vue',
|
||||||
'^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
|
'^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
|
||||||
'^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
|
'^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
|
||||||
'z-paging/components/z-paging$1/z-paging$1.vue',
|
'z-paging/components/z-paging$1/z-paging$1.vue',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 如果不需要tabBar,可以注释掉这个配置,或者直接删除
|
// tabbar 的配置统一在 “./src/layouts/fg-tabbar/tabbarList.ts” 文件中
|
||||||
tabBar: {
|
tabBar: tabBar as any,
|
||||||
color: '#999999',
|
|
||||||
selectedColor: '#018d71',
|
|
||||||
backgroundColor: '#F8F8F8',
|
|
||||||
borderStyle: 'black',
|
|
||||||
height: '50px',
|
|
||||||
fontSize: '10px',
|
|
||||||
iconWidth: '24px',
|
|
||||||
spacing: '3px',
|
|
||||||
list: [
|
|
||||||
{
|
|
||||||
iconPath: 'static/tabbar/home.png',
|
|
||||||
selectedIconPath: 'static/tabbar/homeHL.png',
|
|
||||||
pagePath: 'pages/index/index',
|
|
||||||
text: '首页',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconPath: 'static/tabbar/example.png',
|
|
||||||
selectedIconPath: 'static/tabbar/exampleHL.png',
|
|
||||||
pagePath: 'pages/about/about',
|
|
||||||
text: '关于',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
13
patches/@dcloudio__uni-h5.patch
Normal file
13
patches/@dcloudio__uni-h5.patch
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/dist/uni-h5.es.js b/dist/uni-h5.es.js
|
||||||
|
index 7421bad97d94ad34a3d4d94292a9ee9071430662..19c6071ee4036ceb8d1cfa09030e471c002d2cda 100644
|
||||||
|
--- a/dist/uni-h5.es.js
|
||||||
|
+++ b/dist/uni-h5.es.js
|
||||||
|
@@ -23410,7 +23410,7 @@ function useShowTabBar(emit2) {
|
||||||
|
const tabBar2 = useTabBar();
|
||||||
|
const showTabBar2 = computed(() => route.meta.isTabBar && tabBar2.shown);
|
||||||
|
updateCssVar({
|
||||||
|
- "--tab-bar-height": tabBar2.height
|
||||||
|
+ "--tab-bar-height": tabBar2?.height || 0
|
||||||
|
});
|
||||||
|
return showTabBar2;
|
||||||
|
}
|
6952
pnpm-lock.yaml
generated
6952
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
patchedDependencies:
|
||||||
|
'@dcloudio/uni-h5': patches/@dcloudio__uni-h5.patch
|
Binary file not shown.
Before Width: | Height: | Size: 116 KiB |
Binary file not shown.
Before Width: | Height: | Size: 134 KiB |
@ -2,8 +2,7 @@
|
|||||||
// # 在升级完后,会自动添加很多无用依赖,这需要删除以减小依赖包体积
|
// # 在升级完后,会自动添加很多无用依赖,这需要删除以减小依赖包体积
|
||||||
// # 只需要执行下面的命令即可
|
// # 只需要执行下面的命令即可
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
const { exec } = require('node:child_process')
|
||||||
const { exec } = require('child_process')
|
|
||||||
|
|
||||||
// 定义要执行的命令
|
// 定义要执行的命令
|
||||||
const dependencies = [
|
const dependencies = [
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
|
import { onHide, onLaunch, onShow } from '@dcloudio/uni-app'
|
||||||
|
import { usePageAuth } from '@/hooks/usePageAuth'
|
||||||
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
|
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
|
||||||
|
|
||||||
|
usePageAuth()
|
||||||
|
|
||||||
onLaunch(() => {
|
onLaunch(() => {
|
||||||
console.log('App Launch')
|
console.log('App Launch')
|
||||||
})
|
})
|
||||||
@ -14,7 +17,6 @@ onHide(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
/* stylelint-disable selector-type-no-unknown */
|
|
||||||
button::after {
|
button::after {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
83
src/api/login.ts
Normal file
83
src/api/login.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import type { ICaptcha, IUpdateInfo, IUpdatePassword, IUserInfoVo, IUserLogin } from './types/login'
|
||||||
|
import { http } from '@/utils/http'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录表单
|
||||||
|
*/
|
||||||
|
export interface ILoginForm {
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
code: string
|
||||||
|
uuid: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取验证码
|
||||||
|
* @returns ICaptcha 验证码
|
||||||
|
*/
|
||||||
|
export function getCode() {
|
||||||
|
return http.get<ICaptcha>('/user/getCode')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录
|
||||||
|
* @param loginForm 登录表单
|
||||||
|
*/
|
||||||
|
export function login(loginForm: ILoginForm) {
|
||||||
|
return http.post<IUserLogin>('/user/login', loginForm)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
*/
|
||||||
|
export function getUserInfo() {
|
||||||
|
return http.get<IUserInfoVo>('/user/info')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
*/
|
||||||
|
export function logout() {
|
||||||
|
return http.get<void>('/user/logout')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改用户信息
|
||||||
|
*/
|
||||||
|
export function updateInfo(data: IUpdateInfo) {
|
||||||
|
return http.post('/user/updateInfo', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改用户密码
|
||||||
|
*/
|
||||||
|
export function updateUserPassword(data: IUpdatePassword) {
|
||||||
|
return http.post('/user/updatePassword', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信登录凭证
|
||||||
|
* @returns Promise 包含微信登录凭证(code)
|
||||||
|
*/
|
||||||
|
export function getWxCode() {
|
||||||
|
return new Promise<UniApp.LoginRes>((resolve, reject) => {
|
||||||
|
uni.login({
|
||||||
|
provider: 'weixin',
|
||||||
|
success: res => resolve(res),
|
||||||
|
fail: err => reject(new Error(err)),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信登录参数
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信登录
|
||||||
|
* @param params 微信登录参数,包含code
|
||||||
|
* @returns Promise 包含登录结果
|
||||||
|
*/
|
||||||
|
export function wxLogin(data: { code: string }) {
|
||||||
|
return http.post<IUserLogin>('/user/wxLogin', data)
|
||||||
|
}
|
57
src/api/types/login.ts
Normal file
57
src/api/types/login.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* 用户信息
|
||||||
|
*/
|
||||||
|
export interface IUserInfoVo {
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
avatar: string
|
||||||
|
token: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录返回的信息
|
||||||
|
*/
|
||||||
|
export interface IUserLogin {
|
||||||
|
id: string
|
||||||
|
username: string
|
||||||
|
token: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取验证码
|
||||||
|
*/
|
||||||
|
export interface ICaptcha {
|
||||||
|
captchaEnabled: boolean
|
||||||
|
uuid: string
|
||||||
|
image: string
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 上传成功的信息
|
||||||
|
*/
|
||||||
|
export interface IUploadSuccessInfo {
|
||||||
|
fileId: number
|
||||||
|
originalName: string
|
||||||
|
fileName: string
|
||||||
|
storagePath: string
|
||||||
|
fileHash: string
|
||||||
|
fileType: string
|
||||||
|
fileBusinessType: string
|
||||||
|
fileSize: number
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 更新用户信息
|
||||||
|
*/
|
||||||
|
export interface IUpdateInfo {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
sex: string
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 更新用户信息
|
||||||
|
*/
|
||||||
|
export interface IUpdatePassword {
|
||||||
|
id: number
|
||||||
|
oldPassword: string
|
||||||
|
newPassword: string
|
||||||
|
confirmPassword: string
|
||||||
|
}
|
58
src/components/fg-navbar/fg-navbar.vue
Normal file
58
src/components/fg-navbar/fg-navbar.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
leftText?: string
|
||||||
|
rightText?: string
|
||||||
|
leftArrow?: boolean
|
||||||
|
bordered?: boolean
|
||||||
|
fixed?: boolean
|
||||||
|
placeholder?: boolean
|
||||||
|
zIndex?: number
|
||||||
|
safeAreaInsetTop?: boolean
|
||||||
|
leftDisabled?: boolean
|
||||||
|
rightDisabled?: boolean
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
leftText: '返回',
|
||||||
|
rightText: '',
|
||||||
|
leftArrow: true,
|
||||||
|
bordered: true,
|
||||||
|
fixed: false,
|
||||||
|
placeholder: true,
|
||||||
|
zIndex: 1,
|
||||||
|
safeAreaInsetTop: true,
|
||||||
|
leftDisabled: false,
|
||||||
|
rightDisabled: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
function handleClickLeft() {
|
||||||
|
uni.navigateBack({
|
||||||
|
fail() {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index/index',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<wd-navbar
|
||||||
|
:left-text="leftText"
|
||||||
|
:right-text="rightText"
|
||||||
|
:left-arrow="leftArrow"
|
||||||
|
:bordered="bordered"
|
||||||
|
:fixed="fixed"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:z-index="zIndex"
|
||||||
|
:safe-area-inset-top="safeAreaInsetTop"
|
||||||
|
:left-disabled="leftDisabled"
|
||||||
|
:right-disabled="rightDisabled"
|
||||||
|
@click-left="handleClickLeft"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<slot />
|
||||||
|
</template>
|
||||||
|
</wd-navbar>
|
||||||
|
</template>
|
7
src/env.d.ts
vendored
7
src/env.d.ts
vendored
@ -2,8 +2,8 @@
|
|||||||
/// <reference types="vite-svg-loader" />
|
/// <reference types="vite-svg-loader" />
|
||||||
|
|
||||||
declare module '*.vue' {
|
declare module '*.vue' {
|
||||||
import { DefineComponent } from 'vue'
|
import type { DefineComponent } from 'vue'
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
|
||||||
const component: DefineComponent<{}, {}, any>
|
const component: DefineComponent<{}, {}, any>
|
||||||
export default component
|
export default component
|
||||||
}
|
}
|
||||||
@ -29,3 +29,6 @@ interface ImportMetaEnv {
|
|||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
readonly env: ImportMetaEnv
|
readonly env: ImportMetaEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare const __VITE_APP_PROXY__: 'true' | 'false'
|
||||||
|
declare const __UNI_PLATFORM__: 'app' | 'h5' | 'mp-alipay' | 'mp-baidu' | 'mp-kuaishou' | 'mp-lark' | 'mp-qq' | 'mp-tiktok' | 'mp-weixin' | 'mp-xiaochengxu'
|
||||||
|
50
src/hooks/usePageAuth.ts
Normal file
50
src/hooks/usePageAuth.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
import { useUserStore } from '@/store'
|
||||||
|
import { needLoginPages as _needLoginPages, getNeedLoginPages } from '@/utils'
|
||||||
|
|
||||||
|
const loginRoute = import.meta.env.VITE_LOGIN_URL
|
||||||
|
const isDev = import.meta.env.DEV
|
||||||
|
function isLogined() {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
return !!userStore.userInfo.username
|
||||||
|
}
|
||||||
|
// 检查当前页面是否需要登录
|
||||||
|
export function usePageAuth() {
|
||||||
|
onLoad((options) => {
|
||||||
|
// 获取当前页面路径
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
const currentPage = pages[pages.length - 1]
|
||||||
|
const currentPath = `/${currentPage.route}`
|
||||||
|
|
||||||
|
// 获取需要登录的页面列表
|
||||||
|
let needLoginPages: string[] = []
|
||||||
|
if (isDev) {
|
||||||
|
needLoginPages = getNeedLoginPages()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
needLoginPages = _needLoginPages
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查当前页面是否需要登录
|
||||||
|
const isNeedLogin = needLoginPages.includes(currentPath)
|
||||||
|
if (!isNeedLogin) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasLogin = isLogined()
|
||||||
|
if (hasLogin) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建重定向URL
|
||||||
|
const queryString = Object.entries(options || {})
|
||||||
|
.map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)
|
||||||
|
.join('&')
|
||||||
|
|
||||||
|
const currentFullPath = queryString ? `${currentPath}?${queryString}` : currentPath
|
||||||
|
const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(currentFullPath)}`
|
||||||
|
|
||||||
|
// 重定向到登录页
|
||||||
|
uni.redirectTo({ url: redirectRoute })
|
||||||
|
})
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { UnwrapRef } from 'vue'
|
import type { UnwrapRef } from 'vue'
|
||||||
|
|
||||||
type IUseRequestOptions<T> = {
|
interface IUseRequestOptions<T> {
|
||||||
/** 是否立即执行 */
|
/** 是否立即执行 */
|
||||||
immediate?: boolean
|
immediate?: boolean
|
||||||
/** 初始化数据 */
|
/** 初始化数据 */
|
||||||
|
@ -1,69 +1,160 @@
|
|||||||
// TODO: 别忘加更改环境变量的 VITE_UPLOAD_BASEURL 地址。
|
import { ref } from 'vue'
|
||||||
import { getEnvBaseUploadUrl } from '@/utils'
|
import { getEnvBaseUploadUrl } from '@/utils'
|
||||||
|
|
||||||
const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
|
const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
|
||||||
|
|
||||||
/**
|
type TfileType = 'image' | 'file'
|
||||||
* useUpload 是一个定制化的请求钩子,用于处理上传图片。
|
type TImage = 'png' | 'jpg' | 'jpeg' | 'webp' | '*'
|
||||||
* @param formData 额外传递给后台的数据,如{name: '菲鸽'}。
|
type TFile = 'doc' | 'docx' | 'ppt' | 'zip' | 'xls' | 'xlsx' | 'txt' | TImage
|
||||||
* @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
|
|
||||||
*/
|
interface TOptions<T extends TfileType> {
|
||||||
export default function useUpload<T = string>(formData: Record<string, any> = {}) {
|
formData?: Record<string, any>
|
||||||
|
maxSize?: number
|
||||||
|
accept?: T extends 'image' ? TImage[] : TFile[]
|
||||||
|
fileType?: T
|
||||||
|
success?: (params: any) => void
|
||||||
|
error?: (err: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function useUpload<T extends TfileType>(options: TOptions<T> = {} as TOptions<T>) {
|
||||||
|
const {
|
||||||
|
formData = {},
|
||||||
|
maxSize = 5 * 1024 * 1024,
|
||||||
|
accept = ['*'],
|
||||||
|
fileType = 'image',
|
||||||
|
success,
|
||||||
|
error: onError,
|
||||||
|
} = options
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref(false)
|
const error = ref<Error | null>(null)
|
||||||
const data = ref<T>()
|
const data = ref<any>(null)
|
||||||
|
|
||||||
|
const handleFileChoose = ({ tempFilePath, size }: { tempFilePath: string, size: number }) => {
|
||||||
|
if (size > maxSize) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `文件大小不能超过 ${maxSize / 1024 / 1024}MB`,
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// const fileExtension = file?.tempFiles?.name?.split('.').pop()?.toLowerCase()
|
||||||
|
// const isTypeValid = accept.some((type) => type === '*' || type.toLowerCase() === fileExtension)
|
||||||
|
|
||||||
|
// if (!isTypeValid) {
|
||||||
|
// uni.showToast({
|
||||||
|
// title: `仅支持 ${accept.join(', ')} 格式的文件`,
|
||||||
|
// icon: 'none',
|
||||||
|
// })
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
uploadFile({
|
||||||
|
tempFilePath,
|
||||||
|
formData,
|
||||||
|
onSuccess: (res) => {
|
||||||
|
const { data: _data } = JSON.parse(res)
|
||||||
|
data.value = _data
|
||||||
|
// console.log('上传成功', res)
|
||||||
|
success?.(_data)
|
||||||
|
},
|
||||||
|
onError: (err) => {
|
||||||
|
error.value = err
|
||||||
|
onError?.(err)
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
loading.value = false
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const run = () => {
|
const run = () => {
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
|
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
|
||||||
// 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
|
// 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
|
||||||
uni.chooseMedia({
|
const chooseFileOptions = {
|
||||||
count: 1,
|
count: 1,
|
||||||
mediaType: ['image'],
|
success: (res: any) => {
|
||||||
success: (res) => {
|
console.log('File selected successfully:', res)
|
||||||
loading.value = true
|
// 小程序中res:{errMsg: "chooseImage:ok", tempFiles: [{fileType: "image", size: 48976, tempFilePath: "http://tmp/5iG1WpIxTaJf3ece38692a337dc06df7eb69ecb49c6b.jpeg"}]}
|
||||||
const tempFilePath = res.tempFiles[0].tempFilePath
|
// h5中res:{errMsg: "chooseImage:ok", tempFilePaths: "blob:http://localhost:9000/f74ab6b8-a14d-4cb6-a10d-fcf4511a0de5", tempFiles: [File]}
|
||||||
uploadFile<T>({ tempFilePath, formData, data, error, loading })
|
// h5的File有以下字段:{name: "girl.jpeg", size: 48976, type: "image/jpeg"}
|
||||||
|
// App中res:{errMsg: "chooseImage:ok", tempFilePaths: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", tempFiles: [File]}
|
||||||
|
// App的File有以下字段:{path: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", size: 48976}
|
||||||
|
let tempFilePath = ''
|
||||||
|
let size = 0
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
tempFilePath = res.tempFiles[0].tempFilePath
|
||||||
|
size = res.tempFiles[0].size
|
||||||
|
// #endif
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
tempFilePath = res.tempFilePaths[0]
|
||||||
|
size = res.tempFiles[0].size
|
||||||
|
// #endif
|
||||||
|
handleFileChoose({ tempFilePath, size })
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err: any) => {
|
||||||
console.error('uni.chooseMedia err->', err)
|
console.error('File selection failed:', err)
|
||||||
error.value = true
|
error.value = err
|
||||||
|
onError?.(err)
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
// #endif
|
|
||||||
// #ifndef MP-WEIXIN
|
if (fileType === 'image') {
|
||||||
uni.chooseImage({
|
// #ifdef MP-WEIXIN
|
||||||
count: 1,
|
uni.chooseMedia({
|
||||||
success: (res) => {
|
...chooseFileOptions,
|
||||||
loading.value = true
|
mediaType: ['image'],
|
||||||
const tempFilePath = res.tempFilePaths[0]
|
})
|
||||||
uploadFile<T>({ tempFilePath, formData, data, error, loading })
|
// #endif
|
||||||
},
|
|
||||||
fail: (err) => {
|
// #ifndef MP-WEIXIN
|
||||||
console.error('uni.chooseImage err->', err)
|
uni.chooseImage(chooseFileOptions)
|
||||||
error.value = true
|
// #endif
|
||||||
},
|
}
|
||||||
})
|
else {
|
||||||
// #endif
|
uni.chooseFile({
|
||||||
|
...chooseFileOptions,
|
||||||
|
type: 'all',
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { loading, error, data, run }
|
return { loading, error, data, run }
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadFile<T>({ tempFilePath, formData, data, error, loading }) {
|
async function uploadFile({
|
||||||
|
tempFilePath,
|
||||||
|
formData,
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
onComplete,
|
||||||
|
}: {
|
||||||
|
tempFilePath: string
|
||||||
|
formData: Record<string, any>
|
||||||
|
onSuccess: (data: any) => void
|
||||||
|
onError: (err: any) => void
|
||||||
|
onComplete: () => void
|
||||||
|
}) {
|
||||||
uni.uploadFile({
|
uni.uploadFile({
|
||||||
url: VITE_UPLOAD_BASEURL,
|
url: VITE_UPLOAD_BASEURL,
|
||||||
filePath: tempFilePath,
|
filePath: tempFilePath,
|
||||||
name: 'file',
|
name: 'file',
|
||||||
formData,
|
formData,
|
||||||
success: (uploadFileRes) => {
|
success: (uploadFileRes) => {
|
||||||
data.value = uploadFileRes.data as T
|
try {
|
||||||
|
const data = uploadFileRes.data
|
||||||
|
onSuccess(data)
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
onError(err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error('uni.uploadFile err->', err)
|
console.error('Upload failed:', err)
|
||||||
error.value = true
|
onError(err)
|
||||||
},
|
|
||||||
complete: () => {
|
|
||||||
loading.value = false
|
|
||||||
},
|
},
|
||||||
|
complete: onComplete,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export { routeInterceptor } from './route'
|
|
||||||
export { requestInterceptor } from './request'
|
|
||||||
export { prototypeInterceptor } from './prototype'
|
export { prototypeInterceptor } from './prototype'
|
||||||
|
export { requestInterceptor } from './request'
|
||||||
|
export { routeInterceptor } from './route'
|
||||||
|
@ -2,10 +2,11 @@ export const prototypeInterceptor = {
|
|||||||
install() {
|
install() {
|
||||||
// 解决低版本手机不识别 array.at() 导致运行报错的问题
|
// 解决低版本手机不识别 array.at() 导致运行报错的问题
|
||||||
if (typeof Array.prototype.at !== 'function') {
|
if (typeof Array.prototype.at !== 'function') {
|
||||||
// eslint-disable-next-line no-extend-native
|
|
||||||
Array.prototype.at = function (index: number) {
|
Array.prototype.at = function (index: number) {
|
||||||
if (index < 0) return this[this.length + index]
|
if (index < 0)
|
||||||
if (index >= this.length) return undefined
|
return this[this.length + index]
|
||||||
|
if (index >= this.length)
|
||||||
|
return undefined
|
||||||
return this[index]
|
return this[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
/* eslint-disable no-param-reassign */
|
|
||||||
import qs from 'qs'
|
|
||||||
import { useUserStore } from '@/store'
|
import { useUserStore } from '@/store'
|
||||||
import { platform } from '@/utils/platform'
|
|
||||||
import { getEnvBaseUrl } from '@/utils'
|
import { getEnvBaseUrl } from '@/utils'
|
||||||
|
import { platform } from '@/utils/platform'
|
||||||
|
import { stringifyQuery } from '@/utils/queryString'
|
||||||
|
|
||||||
export type CustomRequestOptions = UniApp.RequestOptions & {
|
export type CustomRequestOptions = UniApp.RequestOptions & {
|
||||||
query?: Record<string, any>
|
query?: Record<string, any>
|
||||||
@ -19,10 +18,11 @@ const httpInterceptor = {
|
|||||||
invoke(options: CustomRequestOptions) {
|
invoke(options: CustomRequestOptions) {
|
||||||
// 接口请求支持通过 query 参数配置 queryString
|
// 接口请求支持通过 query 参数配置 queryString
|
||||||
if (options.query) {
|
if (options.query) {
|
||||||
const queryStr = qs.stringify(options.query)
|
const queryStr = stringifyQuery(options.query)
|
||||||
if (options.url.includes('?')) {
|
if (options.url.includes('?')) {
|
||||||
options.url += `&${queryStr}`
|
options.url += `&${queryStr}`
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
options.url += `?${queryStr}`
|
options.url += `?${queryStr}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,7 +33,8 @@ const httpInterceptor = {
|
|||||||
if (JSON.parse(__VITE_APP_PROXY__)) {
|
if (JSON.parse(__VITE_APP_PROXY__)) {
|
||||||
// 自动拼接代理前缀
|
// 自动拼接代理前缀
|
||||||
options.url = import.meta.env.VITE_APP_PROXY_PREFIX + options.url
|
options.url = import.meta.env.VITE_APP_PROXY_PREFIX + options.url
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
options.url = baseUrl + options.url
|
options.url = baseUrl + options.url
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
|
@ -5,14 +5,14 @@
|
|||||||
* 我这里应为大部分都可以随便进入,所以使用黑名单
|
* 我这里应为大部分都可以随便进入,所以使用黑名单
|
||||||
*/
|
*/
|
||||||
import { useUserStore } from '@/store'
|
import { useUserStore } from '@/store'
|
||||||
import { needLoginPages as _needLoginPages, getNeedLoginPages, getLastPage } from '@/utils'
|
import { needLoginPages as _needLoginPages, getLastPage, getNeedLoginPages } from '@/utils'
|
||||||
|
|
||||||
// TODO Check
|
// TODO Check
|
||||||
const loginRoute = '/pages/login/index'
|
const loginRoute = import.meta.env.VITE_LOGIN_URL
|
||||||
|
|
||||||
const isLogined = () => {
|
function isLogined() {
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
return userStore.isLogined
|
return !!userStore.userInfo.username
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDev = import.meta.env.DEV
|
const isDev = import.meta.env.DEV
|
||||||
@ -37,7 +37,8 @@ const navigateToInterceptor = {
|
|||||||
// 为了防止开发时出现BUG,这里每次都获取一下。生产环境可以移到函数外,性能更好
|
// 为了防止开发时出现BUG,这里每次都获取一下。生产环境可以移到函数外,性能更好
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
needLoginPages = getNeedLoginPages()
|
needLoginPages = getNeedLoginPages()
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
needLoginPages = _needLoginPages
|
needLoginPages = _needLoginPages
|
||||||
}
|
}
|
||||||
const isNeedLogin = needLoginPages.includes(path)
|
const isNeedLogin = needLoginPages.includes(path)
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
<template>
|
|
||||||
<wd-config-provider :themeVars="themeVars">
|
|
||||||
<slot />
|
|
||||||
<wd-toast />
|
|
||||||
<wd-message-box />
|
|
||||||
</wd-config-provider>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ConfigProviderThemeVars } from 'wot-design-uni'
|
import type { ConfigProviderThemeVars } from 'wot-design-uni'
|
||||||
|
|
||||||
@ -15,3 +7,11 @@ const themeVars: ConfigProviderThemeVars = {
|
|||||||
// buttonPrimaryColor: '#07c160',
|
// buttonPrimaryColor: '#07c160',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<wd-config-provider :theme-vars="themeVars">
|
||||||
|
<slot />
|
||||||
|
<wd-toast />
|
||||||
|
<wd-message-box />
|
||||||
|
</wd-config-provider>
|
||||||
|
</template>
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
<template>
|
|
||||||
<wd-config-provider :themeVars="themeVars">
|
|
||||||
<slot />
|
|
||||||
<wd-toast />
|
|
||||||
<wd-message-box />
|
|
||||||
</wd-config-provider>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { ConfigProviderThemeVars } from 'wot-design-uni'
|
import type { ConfigProviderThemeVars } from 'wot-design-uni'
|
||||||
|
|
||||||
@ -15,3 +7,11 @@ const themeVars: ConfigProviderThemeVars = {
|
|||||||
// buttonPrimaryColor: '#07c160',
|
// buttonPrimaryColor: '#07c160',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<wd-config-provider :theme-vars="themeVars">
|
||||||
|
<slot />
|
||||||
|
<wd-toast />
|
||||||
|
<wd-message-box />
|
||||||
|
</wd-config-provider>
|
||||||
|
</template>
|
||||||
|
67
src/layouts/fg-tabbar/fg-tabbar.vue
Normal file
67
src/layouts/fg-tabbar/fg-tabbar.vue
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { tabbarStore } from './tabbar'
|
||||||
|
// 'i-carbon-code',
|
||||||
|
import { tabbarList as _tabBarList, cacheTabbarEnable, selectedTabbarStrategy } from './tabbarList'
|
||||||
|
|
||||||
|
// @ts-expect-error 预知的判断
|
||||||
|
const customTabbarEnable = selectedTabbarStrategy === 1 || selectedTabbarStrategy === 2
|
||||||
|
/** tabbarList 里面的 path 从 pages.config.ts 得到 */
|
||||||
|
const tabbarList = _tabBarList.map(item => ({ ...item, path: `/${item.pagePath}` }))
|
||||||
|
function selectTabBar({ value: index }: { value: number }) {
|
||||||
|
const url = tabbarList[index].path
|
||||||
|
tabbarStore.setCurIdx(index)
|
||||||
|
if (cacheTabbarEnable) {
|
||||||
|
uni.switchTab({ url })
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uni.navigateTo({ url })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onLoad(() => {
|
||||||
|
// 解决原生 tabBar 未隐藏导致有2个 tabBar 的问题
|
||||||
|
// @ts-expect-error 预知的判断
|
||||||
|
const hideRedundantTabbarEnable = selectedTabbarStrategy === 1
|
||||||
|
hideRedundantTabbarEnable
|
||||||
|
&& uni.hideTabBar({
|
||||||
|
fail(err) {
|
||||||
|
console.log('hideTabBar fail: ', err)
|
||||||
|
},
|
||||||
|
success(res) {
|
||||||
|
console.log('hideTabBar success: ', res)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<wd-tabbar
|
||||||
|
v-if="customTabbarEnable"
|
||||||
|
v-model="tabbarStore.curIdx"
|
||||||
|
bordered
|
||||||
|
safeareainsetbottom
|
||||||
|
placeholder
|
||||||
|
fixed
|
||||||
|
@change="selectTabBar"
|
||||||
|
>
|
||||||
|
<block v-for="(item, idx) in tabbarList" :key="item.path">
|
||||||
|
<wd-tabbar-item v-if="item.iconType === 'uiLib'" :title="item.text" :icon="item.icon" />
|
||||||
|
<wd-tabbar-item
|
||||||
|
v-else-if="item.iconType === 'unocss' || item.iconType === 'iconfont'"
|
||||||
|
:title="item.text"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<view
|
||||||
|
h-40rpx
|
||||||
|
w-40rpx
|
||||||
|
:class="[item.icon, idx === tabbarStore.curIdx ? 'is-active' : 'is-inactive']"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</wd-tabbar-item>
|
||||||
|
<wd-tabbar-item v-else-if="item.iconType === 'local'" :title="item.text">
|
||||||
|
<template #icon>
|
||||||
|
<image :src="item.icon" h-40rpx w-40rpx />
|
||||||
|
</template>
|
||||||
|
</wd-tabbar-item>
|
||||||
|
</block>
|
||||||
|
</wd-tabbar>
|
||||||
|
</template>
|
16
src/layouts/fg-tabbar/tabbar.md
Normal file
16
src/layouts/fg-tabbar/tabbar.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# tabbar 说明
|
||||||
|
|
||||||
|
`tabbar` 分为 `4 种` 情况:
|
||||||
|
|
||||||
|
- `完全原生 tabbar`,使用 `switchTab` 切换 tabbar,`tabbar` 页面有缓存。
|
||||||
|
- 优势:原生自带的 tabbar,最先渲染,有缓存。
|
||||||
|
- 劣势:只能使用 2 组图片来切换选中和非选中状态,修改颜色只能重新换图片(或者用 iconfont)。
|
||||||
|
- `半自定义 tabbar`,使用 `switchTab` 切换 tabbar,`tabbar` 页面有缓存。使用了第三方 UI 库的 `tabbar` 组件,并隐藏了原生 `tabbar` 的显示。
|
||||||
|
- 优势:可以随意配置自己想要的 `svg icon`,切换字体颜色方便。有缓存。可以实现各种花里胡哨的动效等。
|
||||||
|
- 劣势:首次点击 tababr 会闪烁。
|
||||||
|
- `全自定义 tabbar`,使用 `navigateTo` 切换 `tabbar`,`tabbar` 页面无缓存。使用了第三方 UI 库的 `tabbar` 组件。
|
||||||
|
- 优势:可以随意配置自己想要的 svg icon,切换字体颜色方便。可以实现各种花里胡哨的动效等。
|
||||||
|
- 劣势:首次点击 `tababr` 会闪烁,无缓存。
|
||||||
|
- `无 tabbar`,只有一个页面入口,底部无 `tabbar` 显示;常用语临时活动页。
|
||||||
|
|
||||||
|
> 注意:花里胡哨的效果需要自己实现,本模版不提供。
|
11
src/layouts/fg-tabbar/tabbar.ts
Normal file
11
src/layouts/fg-tabbar/tabbar.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* tabbar 状态,增加 storageSync 保证刷新浏览器时在正确的 tabbar 页面
|
||||||
|
* 使用reactive简单状态,而不是 pinia 全局状态
|
||||||
|
*/
|
||||||
|
export const tabbarStore = reactive({
|
||||||
|
curIdx: uni.getStorageSync('app-tabbar-index') || 0,
|
||||||
|
setCurIdx(idx: number) {
|
||||||
|
this.curIdx = idx
|
||||||
|
uni.setStorageSync('app-tabbar-index', idx)
|
||||||
|
},
|
||||||
|
})
|
65
src/layouts/fg-tabbar/tabbarList.ts
Normal file
65
src/layouts/fg-tabbar/tabbarList.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* tabbar 选择的策略,更详细的介绍见 tabbar.md 文件
|
||||||
|
* 0: 'NATIVE_TABBAR' `完全原生 tabbar`
|
||||||
|
* 2: 'FULL_CUSTOM_TABBAR' `全自定义 tabbar`
|
||||||
|
* 1: 'HALF_CUSTOM_TABBAR' `半自定义 tabbar`
|
||||||
|
* 3: 'NO_TABBAR' `无 tabbar`
|
||||||
|
*
|
||||||
|
* 温馨提示:本文件的任何代码更改了之后,都需要重新运行,否则 pages.json 不会更新导致错误
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO:通过这里切换使用tabbar的策略
|
||||||
|
export const selectedTabbarStrategy = 0
|
||||||
|
|
||||||
|
// 0 和 1 时,需要tabbar缓存
|
||||||
|
export const cacheTabbarEnable = selectedTabbarStrategy < 2
|
||||||
|
|
||||||
|
// selectedTabbarStrategy==0 时,需要填 iconPath 和 selectedIconPath
|
||||||
|
// selectedTabbarStrategy==1 or 2 时,需要填 icon 和 iconType
|
||||||
|
// selectedTabbarStrategy==3 时,tabbarList 不生效
|
||||||
|
export const tabbarList = [
|
||||||
|
{
|
||||||
|
iconPath: 'static/tabbar/home.png',
|
||||||
|
selectedIconPath: 'static/tabbar/homeHL.png',
|
||||||
|
pagePath: 'pages/index/index',
|
||||||
|
text: '首页',
|
||||||
|
icon: 'home',
|
||||||
|
iconType: 'uiLib',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconPath: 'static/tabbar/example.png',
|
||||||
|
selectedIconPath: 'static/tabbar/exampleHL.png',
|
||||||
|
pagePath: 'pages/about/about',
|
||||||
|
text: '关于',
|
||||||
|
icon: 'i-carbon-code',
|
||||||
|
// 注意 unocss 的图标需要在 页面上引入一下,或者配置到 unocss.config.ts 的 safelist 中
|
||||||
|
iconType: 'unocss',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// pagePath: 'pages/my/index',
|
||||||
|
// text: '我的',
|
||||||
|
// icon: '/static/logo.svg',
|
||||||
|
// iconType: 'local',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// pagePath: 'pages/mine/index',
|
||||||
|
// text: '我的',
|
||||||
|
// icon: 'iconfont icon-my',
|
||||||
|
// iconType: 'iconfont',
|
||||||
|
// },
|
||||||
|
]
|
||||||
|
|
||||||
|
const _tabbar = {
|
||||||
|
color: '#999999',
|
||||||
|
selectedColor: '#018d71',
|
||||||
|
backgroundColor: '#F8F8F8',
|
||||||
|
borderStyle: 'black',
|
||||||
|
height: '50px',
|
||||||
|
fontSize: '10px',
|
||||||
|
iconWidth: '24px',
|
||||||
|
spacing: '3px',
|
||||||
|
list: tabbarList,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0和1 需要显示底部的tabbar的各种配置,以利用缓存
|
||||||
|
export const tabBar = cacheTabbarEnable ? _tabbar : undefined
|
19
src/layouts/tabbar.vue
Normal file
19
src/layouts/tabbar.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import type { ConfigProviderThemeVars } from 'wot-design-uni'
|
||||||
|
import FgTabbar from './fg-tabbar/fg-tabbar.vue'
|
||||||
|
|
||||||
|
const themeVars: ConfigProviderThemeVars = {
|
||||||
|
// colorTheme: 'red',
|
||||||
|
// buttonPrimaryBgColor: '#07c160',
|
||||||
|
// buttonPrimaryColor: '#07c160',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<wd-config-provider :theme-vars="themeVars">
|
||||||
|
<slot />
|
||||||
|
<FgTabbar />
|
||||||
|
<wd-toast />
|
||||||
|
<wd-message-box />
|
||||||
|
</wd-config-provider>
|
||||||
|
</template>
|
@ -1,11 +1,11 @@
|
|||||||
import '@/style/index.scss'
|
|
||||||
import { VueQueryPlugin } from '@tanstack/vue-query'
|
import { VueQueryPlugin } from '@tanstack/vue-query'
|
||||||
import 'virtual:uno.css'
|
|
||||||
import { createSSRApp } from 'vue'
|
import { createSSRApp } from 'vue'
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import { prototypeInterceptor, requestInterceptor, routeInterceptor } from './interceptors'
|
import { prototypeInterceptor, requestInterceptor, routeInterceptor } from './interceptors'
|
||||||
|
|
||||||
import store from './store'
|
import store from './store'
|
||||||
|
import '@/style/index.scss'
|
||||||
|
import 'virtual:uno.css'
|
||||||
|
|
||||||
export function createApp() {
|
export function createApp() {
|
||||||
const app = createSSRApp(App)
|
const app = createSSRApp(App)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "unibest",
|
"name": "unibest",
|
||||||
"appid": "H57F2ACE4",
|
"appid": "__UNI__D1E5001",
|
||||||
"description": "",
|
"description": "",
|
||||||
"versionName": "1.0.0",
|
"versionName": "1.0.0",
|
||||||
"versionCode": "100",
|
"versionCode": "100",
|
||||||
@ -85,7 +85,9 @@
|
|||||||
"mp-weixin": {
|
"mp-weixin": {
|
||||||
"appid": "wxa2abb91f64032a2b",
|
"appid": "wxa2abb91f64032a2b",
|
||||||
"setting": {
|
"setting": {
|
||||||
"urlCheck": false
|
"urlCheck": false,
|
||||||
|
"es6": true,
|
||||||
|
"minified": true
|
||||||
},
|
},
|
||||||
"usingComponents": true
|
"usingComponents": true
|
||||||
},
|
},
|
||||||
|
@ -1,20 +1,27 @@
|
|||||||
<route lang="json5" type="page">
|
<route lang="json5" type="page">
|
||||||
{
|
{
|
||||||
style: { navigationBarTitleText: '分包页面 标题' },
|
style: {
|
||||||
|
navigationStyle: 'default',
|
||||||
|
navigationBarTitleText: '分包页面 标题',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</route>
|
</route>
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="text-center">
|
|
||||||
<view class="m-8">http://localhost:9000/#/pages-sub/demo/index</view>
|
|
||||||
<view class="text-green-500">分包页面demo</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// code here
|
// code here
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="text-center">
|
||||||
|
<view class="m-8">
|
||||||
|
http://localhost:9000/#/pages-sub/demo/index
|
||||||
|
</view>
|
||||||
|
<view class="text-green-500">
|
||||||
|
分包页面demo
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
//
|
//
|
||||||
</style>
|
</style>
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"easycom": {
|
"easycom": {
|
||||||
"autoscan": true,
|
"autoscan": true,
|
||||||
"custom": {
|
"custom": {
|
||||||
|
"^fg-(.*)": "@/components/fg-$1/fg-$1.vue",
|
||||||
"^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue",
|
"^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue",
|
||||||
"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
|
"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
|
||||||
}
|
}
|
||||||
@ -27,13 +28,17 @@
|
|||||||
"iconPath": "static/tabbar/home.png",
|
"iconPath": "static/tabbar/home.png",
|
||||||
"selectedIconPath": "static/tabbar/homeHL.png",
|
"selectedIconPath": "static/tabbar/homeHL.png",
|
||||||
"pagePath": "pages/index/index",
|
"pagePath": "pages/index/index",
|
||||||
"text": "首页"
|
"text": "首页",
|
||||||
|
"icon": "home",
|
||||||
|
"iconType": "uiLib"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"iconPath": "static/tabbar/example.png",
|
"iconPath": "static/tabbar/example.png",
|
||||||
"selectedIconPath": "static/tabbar/exampleHL.png",
|
"selectedIconPath": "static/tabbar/exampleHL.png",
|
||||||
"pagePath": "pages/about/about",
|
"pagePath": "pages/about/about",
|
||||||
"text": "关于"
|
"text": "关于",
|
||||||
|
"icon": "i-carbon-code",
|
||||||
|
"iconType": "unocss"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -41,6 +46,7 @@
|
|||||||
{
|
{
|
||||||
"path": "pages/index/index",
|
"path": "pages/index/index",
|
||||||
"type": "home",
|
"type": "home",
|
||||||
|
"layout": "tabbar",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationStyle": "custom",
|
"navigationStyle": "custom",
|
||||||
"navigationBarTitleText": "首页"
|
"navigationBarTitleText": "首页"
|
||||||
@ -49,10 +55,11 @@
|
|||||||
{
|
{
|
||||||
"path": "pages/about/about",
|
"path": "pages/about/about",
|
||||||
"type": "page",
|
"type": "page",
|
||||||
|
"layout": "tabbar",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "关于"
|
"navigationBarTitleText": "关于"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"subPackages": []
|
"subPackages": []
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,46 @@
|
|||||||
<route lang="json5">
|
<route lang="json5">
|
||||||
{
|
{
|
||||||
|
layout: 'tabbar',
|
||||||
style: {
|
style: {
|
||||||
navigationBarTitleText: '关于',
|
navigationBarTitleText: '关于',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</route>
|
</route>
|
||||||
|
|
||||||
<template>
|
|
||||||
<view
|
|
||||||
class="bg-white overflow-hidden pt-2 px-4"
|
|
||||||
:style="{ marginTop: safeAreaInsets?.top + 'px' }"
|
|
||||||
>
|
|
||||||
<view class="text-center text-3xl mt-8">
|
|
||||||
鸽友们好,我是
|
|
||||||
<text class="text-red-500">菲鸽</text>
|
|
||||||
</view>
|
|
||||||
<RequestComp />
|
|
||||||
<UploadComp />
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import RequestComp from './components/request.vue'
|
import RequestComp from './components/request.vue'
|
||||||
import UploadComp from './components/upload.vue'
|
import UploadComp from './components/upload.vue'
|
||||||
|
|
||||||
// 获取屏幕边界到安全区域距离
|
// 获取屏幕边界到安全区域距离
|
||||||
const { safeAreaInsets } = uni.getSystemInfoSync()
|
const { safeAreaInsets } = uni.getSystemInfoSync()
|
||||||
|
|
||||||
|
// 奇怪:同样的代码放在 vue 里面不会校验到错误,放在 .ts 文件里面会校验到错误
|
||||||
|
// const testOxlint = (name: string) => {
|
||||||
|
// console.log('oxlint')
|
||||||
|
// }
|
||||||
|
// testOxlint('oxlint')
|
||||||
|
console.log('about')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<view class="mt-8 text-center text-3xl">
|
||||||
|
鸽友们好,我是
|
||||||
|
<text class="text-red-500">
|
||||||
|
菲鸽
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<RequestComp />
|
||||||
|
<UploadComp />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.test-css {
|
.test-css {
|
||||||
|
// 16rpx=>0.5rem
|
||||||
|
padding-bottom: 16rpx;
|
||||||
// mt-4=>1rem=>16px;
|
// mt-4=>1rem=>16px;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -7,38 +7,11 @@
|
|||||||
}
|
}
|
||||||
</route>
|
</route>
|
||||||
|
|
||||||
<template>
|
|
||||||
<view class="p-6 text-center">
|
|
||||||
<view class="my-2">使用的是 laf 云后台</view>
|
|
||||||
<view class="text-green-400">我的推荐码,可以获得佣金</view>
|
|
||||||
|
|
||||||
<!-- #ifdef H5 -->
|
|
||||||
<view class="my-2">
|
|
||||||
<a class="my-2" :href="recommendUrl" target="_blank">{{ recommendUrl }}</a>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<!-- #ifndef H5 -->
|
|
||||||
<view class="my-2 text-left text-sm">{{ recommendUrl }}</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<!-- http://localhost:9000/#/pages/index/request -->
|
|
||||||
<wd-button @click="run" class="my-6">发送请求</wd-button>
|
|
||||||
<view class="h-16">
|
|
||||||
<view v-if="loading">loading...</view>
|
|
||||||
<block v-else>
|
|
||||||
<view class="text-xl">请求数据如下</view>
|
|
||||||
<view class="text-green leading-8">{{ JSON.stringify(data) }}</view>
|
|
||||||
</block>
|
|
||||||
</view>
|
|
||||||
<wd-button type="error" @click="reset" class="my-6" :disabled="!data">重置数据</wd-button>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getFooAPI, postFooAPI, IFooItem } from '@/service/index/foo'
|
import type { IFooItem } from '@/service/index/foo'
|
||||||
import { findPetsByStatusQueryOptions } from '@/service/app'
|
import { getFooAPI } from '@/service/index/foo'
|
||||||
import { useQuery } from '@tanstack/vue-query'
|
// import { findPetsByStatusQueryOptions } from '@/service/app'
|
||||||
|
// import { useQuery } from '@tanstack/vue-query'
|
||||||
|
|
||||||
const recommendUrl = ref('http://laf.run/signup?code=ohaOgIX')
|
const recommendUrl = ref('http://laf.run/signup?code=ohaOgIX')
|
||||||
|
|
||||||
@ -54,14 +27,58 @@ const { loading, error, data, run } = useRequest<IFooItem>(() => getFooAPI('菲
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 使用 vue-query 的 useQuery 来请求数据,只做参考,是否使用请根据实际情况而定
|
// 使用 vue-query 的 useQuery 来请求数据,只做参考,是否使用请根据实际情况而定
|
||||||
const {
|
// const {
|
||||||
data: data2,
|
// data: data2,
|
||||||
error: error2,
|
// error: error2,
|
||||||
isLoading: isLoading2,
|
// isLoading: isLoading2,
|
||||||
refetch,
|
// refetch,
|
||||||
} = useQuery(findPetsByStatusQueryOptions({ params: { status: ['available'] } }))
|
// } = useQuery(findPetsByStatusQueryOptions({ params: { status: ['available'] } }))
|
||||||
|
|
||||||
const reset = () => {
|
function reset() {
|
||||||
data.value = initialData
|
data.value = initialData
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="p-6 text-center">
|
||||||
|
<view class="my-2">
|
||||||
|
使用的是 laf 云后台
|
||||||
|
</view>
|
||||||
|
<view class="text-green-400">
|
||||||
|
我的推荐码,可以获得佣金
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- #ifdef H5 -->
|
||||||
|
<view class="my-2">
|
||||||
|
<a class="my-2" :href="recommendUrl" target="_blank">{{ recommendUrl }}</a>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<!-- #ifndef H5 -->
|
||||||
|
<view class="my-2 text-left text-sm">
|
||||||
|
{{ recommendUrl }}
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<!-- http://localhost:9000/#/pages/index/request -->
|
||||||
|
<wd-button class="my-6" @click="run">
|
||||||
|
发送请求
|
||||||
|
</wd-button>
|
||||||
|
<view class="h-16">
|
||||||
|
<view v-if="loading">
|
||||||
|
loading...
|
||||||
|
</view>
|
||||||
|
<block v-else>
|
||||||
|
<view class="text-xl">
|
||||||
|
请求数据如下
|
||||||
|
</view>
|
||||||
|
<view class="text-green leading-8">
|
||||||
|
{{ JSON.stringify(data) }}
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
<wd-button type="error" class="my-6" :disabled="!data" @click="reset">
|
||||||
|
重置数据
|
||||||
|
</wd-button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
@ -7,24 +7,32 @@
|
|||||||
}
|
}
|
||||||
</route>
|
</route>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const { loading, data, run } = useUpload()
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="p-4 text-center">
|
<view class="p-4 text-center">
|
||||||
<wd-button @click="run">选择图片并上传</wd-button>
|
<wd-button @click="run">
|
||||||
<view v-if="loading" class="text-blue h-10">上传...</view>
|
选择图片并上传
|
||||||
|
</wd-button>
|
||||||
|
<view v-if="loading" class="h-10 text-blue">
|
||||||
|
上传...
|
||||||
|
</view>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<view class="m-2">上传后返回的接口数据:</view>
|
<view class="m-2">
|
||||||
<view class="m-2">{{ data }}</view>
|
上传后返回的接口数据:
|
||||||
<view class="h-80 w-full">
|
</view>
|
||||||
<image v-if="data" :src="data || data" mode="scaleToFill" />
|
<view class="m-2">
|
||||||
|
{{ data }}
|
||||||
|
</view>
|
||||||
|
<view v-if="data" class="h-80 w-full">
|
||||||
|
<image :src="data.url" mode="scaleToFill" />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
const { loading, data, run } = useUpload({ user: '菲鸽' })
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
//
|
//
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,34 +1,14 @@
|
|||||||
<!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page;推荐使用json5,更强大,且允许注释 -->
|
<!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page;推荐使用json5,更强大,且允许注释 -->
|
||||||
<route lang="json5" type="home">
|
<route lang="json5" type="home">
|
||||||
{
|
{
|
||||||
|
layout: 'tabbar',
|
||||||
style: {
|
style: {
|
||||||
|
// 'custom' 表示开启自定义导航栏,默认 'default'
|
||||||
navigationStyle: 'custom',
|
navigationStyle: 'custom',
|
||||||
navigationBarTitleText: '首页',
|
navigationBarTitleText: '首页',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</route>
|
</route>
|
||||||
<template>
|
|
||||||
<view
|
|
||||||
class="bg-white overflow-hidden pt-2 px-4"
|
|
||||||
:style="{ marginTop: safeAreaInsets?.top + 'px' }"
|
|
||||||
>
|
|
||||||
<view class="mt-12">
|
|
||||||
<image src="/static/logo.svg" alt="" class="w-28 h-28 block mx-auto" />
|
|
||||||
</view>
|
|
||||||
<view class="text-center text-4xl main-title-color mt-4">unibest</view>
|
|
||||||
<view class="text-center text-2xl mt-2 mb-8">最好用的 uniapp 开发模板</view>
|
|
||||||
|
|
||||||
<view class="text-justify max-w-100 m-auto text-4 indent mb-2">{{ description }}</view>
|
|
||||||
<view class="text-center mt-8">
|
|
||||||
当前平台是:
|
|
||||||
<text class="text-green-500">{{ PLATFORM.platform }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="text-center mt-4">
|
|
||||||
模板分支是:
|
|
||||||
<text class="text-green-500">base</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import PLATFORM from '@/utils/platform'
|
import PLATFORM from '@/utils/platform'
|
||||||
@ -38,19 +18,78 @@ defineOptions({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 获取屏幕边界到安全区域距离
|
// 获取屏幕边界到安全区域距离
|
||||||
const { safeAreaInsets } = uni.getSystemInfoSync()
|
let safeAreaInsets
|
||||||
|
let systemInfo
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 微信小程序使用新的API
|
||||||
|
systemInfo = uni.getWindowInfo()
|
||||||
|
safeAreaInsets = systemInfo.safeArea
|
||||||
|
? {
|
||||||
|
top: systemInfo.safeArea.top,
|
||||||
|
right: systemInfo.windowWidth - systemInfo.safeArea.right,
|
||||||
|
bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
|
||||||
|
left: systemInfo.safeArea.left,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
// 其他平台继续使用uni API
|
||||||
|
systemInfo = uni.getSystemInfoSync()
|
||||||
|
safeAreaInsets = systemInfo.safeAreaInsets
|
||||||
|
// #endif
|
||||||
const author = ref('菲鸽')
|
const author = ref('菲鸽')
|
||||||
const description = ref(
|
const description = ref(
|
||||||
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite4 + UnoCss + UniUI + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
|
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite5 + UnoCss + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
|
||||||
)
|
)
|
||||||
// 测试 uni API 自动引入
|
// 测试 uni API 自动引入
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
console.log('项目作者:', author.value)
|
console.log('项目作者:', author.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
console.log('index')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<template>
|
||||||
.main-title-color {
|
<view class="bg-white px-4 pt-2" :style="{ marginTop: `${safeAreaInsets?.top}px` }">
|
||||||
color: #d14328;
|
<view class="mt-10">
|
||||||
}
|
<image src="/static/logo.svg" alt="" class="mx-auto block h-28 w-28" />
|
||||||
</style>
|
</view>
|
||||||
|
<view class="mt-4 text-center text-4xl text-[#d14328]">
|
||||||
|
unibest
|
||||||
|
</view>
|
||||||
|
<view class="mb-8 mt-2 text-center text-2xl">
|
||||||
|
最好用的 uniapp 开发模板
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="m-auto mb-2 max-w-100 text-justify indent text-4">
|
||||||
|
{{ description }}
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
作者:
|
||||||
|
<text class="text-green-500">
|
||||||
|
菲鸽
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
官网地址:
|
||||||
|
<text class="text-green-500">
|
||||||
|
https://unibest.tech
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="mt-6 h-1px bg-#eee" />
|
||||||
|
<view class="mt-8 text-center">
|
||||||
|
当前平台是:
|
||||||
|
<text class="text-green-500">
|
||||||
|
{{ PLATFORM.platform }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 text-center">
|
||||||
|
模板分支是:
|
||||||
|
<text class="text-green-500">
|
||||||
|
base
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
@ -1,27 +1,28 @@
|
|||||||
import { http } from '@/utils/http'
|
import { http } from '@/utils/http'
|
||||||
|
|
||||||
export interface IFooItem {
|
export interface IFooItem {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** GET 请求 */
|
/** GET 请求 */
|
||||||
export const getFooAPI = (name: string) => {
|
export function getFooAPI(name: string) {
|
||||||
return http.get<IFooItem>('/foo', { name })
|
return http.get<IFooItem>('/foo', { name })
|
||||||
}
|
}
|
||||||
/** GET 请求;支持 传递 header 的范例 */
|
/** GET 请求;支持 传递 header 的范例 */
|
||||||
export const getFooAPI2 = (name: string) => {
|
export function getFooAPI2(name: string) {
|
||||||
return http.get<IFooItem>('/foo', { name }, { 'Content-Type-100': '100' })
|
return http.get<IFooItem>('/foo', { name }, { 'Content-Type-100': '100' })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** POST 请求 */
|
/** POST 请求 */
|
||||||
export const postFooAPI = (name: string) => {
|
export function postFooAPI(name: string) {
|
||||||
return http.post<IFooItem>('/foo', { name })
|
return http.post<IFooItem>('/foo', { name })
|
||||||
}
|
}
|
||||||
/** POST 请求;需要传递 query 参数的范例;微信小程序经常有同时需要query参数和body参数的场景 */
|
/** POST 请求;需要传递 query 参数的范例;微信小程序经常有同时需要query参数和body参数的场景 */
|
||||||
export const postFooAPI2 = (name: string) => {
|
export function postFooAPI2(name: string) {
|
||||||
return http.post<IFooItem>('/foo', { name })
|
return http.post<IFooItem>('/foo', { name })
|
||||||
}
|
}
|
||||||
/** POST 请求;支持 传递 header 的范例 */
|
/** POST 请求;支持 传递 header 的范例 */
|
||||||
export const postFooAPI3 = (name: string) => {
|
export function postFooAPI3(name: string) {
|
||||||
return http.post<IFooItem>('/foo', { name }, { name }, { 'Content-Type-100': '100' })
|
return http.post<IFooItem>('/foo', { name }, { name }, { 'Content-Type-100': '100' })
|
||||||
}
|
}
|
||||||
|
BIN
src/static/images/avatar.jpg
Normal file
BIN
src/static/images/avatar.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 57 KiB |
BIN
src/static/images/default-avatar.png
Normal file
BIN
src/static/images/default-avatar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 560 B |
@ -1,32 +1,108 @@
|
|||||||
|
import type { IUserInfoVo } from '@/api/types/login'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import {
|
||||||
|
getUserInfo as _getUserInfo,
|
||||||
|
login as _login,
|
||||||
|
logout as _logout,
|
||||||
|
wxLogin as _wxLogin,
|
||||||
|
getWxCode,
|
||||||
|
} from '@/api/login'
|
||||||
|
import { toast } from '@/utils/toast'
|
||||||
|
|
||||||
const initState = { nickname: '', avatar: '' }
|
// 初始化状态
|
||||||
|
const userInfoState: IUserInfoVo = {
|
||||||
|
id: 0,
|
||||||
|
username: '',
|
||||||
|
avatar: '/static/images/default-avatar.png',
|
||||||
|
token: '',
|
||||||
|
}
|
||||||
|
|
||||||
export const useUserStore = defineStore(
|
export const useUserStore = defineStore(
|
||||||
'user',
|
'user',
|
||||||
() => {
|
() => {
|
||||||
const userInfo = ref<IUserInfo>({ ...initState })
|
// 定义用户信息
|
||||||
|
const userInfo = ref<IUserInfoVo>({ ...userInfoState })
|
||||||
const setUserInfo = (val: IUserInfo) => {
|
// 设置用户信息
|
||||||
|
const setUserInfo = (val: IUserInfoVo) => {
|
||||||
|
console.log('设置用户信息', val)
|
||||||
|
// 若头像为空 则使用默认头像
|
||||||
|
if (!val.avatar) {
|
||||||
|
val.avatar = userInfoState.avatar
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val.avatar = 'https://oss.laf.run/ukw0y1-site/avatar.jpg?feige'
|
||||||
|
}
|
||||||
userInfo.value = val
|
userInfo.value = val
|
||||||
}
|
}
|
||||||
|
const setUserAvatar = (avatar: string) => {
|
||||||
|
userInfo.value.avatar = avatar
|
||||||
|
console.log('设置用户头像', avatar)
|
||||||
|
console.log('userInfo', userInfo.value)
|
||||||
|
}
|
||||||
|
// 删除用户信息
|
||||||
|
const removeUserInfo = () => {
|
||||||
|
userInfo.value = { ...userInfoState }
|
||||||
|
uni.removeStorageSync('userInfo')
|
||||||
|
uni.removeStorageSync('token')
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
*/
|
||||||
|
const getUserInfo = async () => {
|
||||||
|
const res = await _getUserInfo()
|
||||||
|
const userInfo = res.data
|
||||||
|
setUserInfo(userInfo)
|
||||||
|
uni.setStorageSync('userInfo', userInfo)
|
||||||
|
uni.setStorageSync('token', userInfo.token)
|
||||||
|
// TODO 这里可以增加获取用户路由的方法 根据用户的角色动态生成路由
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 用户登录
|
||||||
|
* @param credentials 登录参数
|
||||||
|
* @returns R<IUserLogin>
|
||||||
|
*/
|
||||||
|
const login = async (credentials: {
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
code: string
|
||||||
|
uuid: string
|
||||||
|
}) => {
|
||||||
|
const res = await _login(credentials)
|
||||||
|
console.log('登录信息', res)
|
||||||
|
toast.success('登录成功')
|
||||||
|
await getUserInfo()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
const clearUserInfo = () => {
|
/**
|
||||||
userInfo.value = { ...initState }
|
* 退出登录 并 删除用户信息
|
||||||
|
*/
|
||||||
|
const logout = async () => {
|
||||||
|
_logout()
|
||||||
|
removeUserInfo()
|
||||||
}
|
}
|
||||||
// 一般没有reset需求,不需要的可以删除
|
/**
|
||||||
const reset = () => {
|
* 微信登录
|
||||||
userInfo.value = { ...initState }
|
*/
|
||||||
|
const wxLogin = async () => {
|
||||||
|
// 获取微信小程序登录的code
|
||||||
|
const data = await getWxCode()
|
||||||
|
console.log('微信登录code', data)
|
||||||
|
|
||||||
|
const res = await _wxLogin(data)
|
||||||
|
await getUserInfo()
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
const isLogined = computed(() => !!userInfo.value.token)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userInfo,
|
userInfo,
|
||||||
setUserInfo,
|
login,
|
||||||
clearUserInfo,
|
wxLogin,
|
||||||
isLogined,
|
getUserInfo,
|
||||||
reset,
|
setUserAvatar,
|
||||||
|
logout,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// @import './iconfont.css';
|
@import './iconfont.css';
|
||||||
|
|
||||||
.test {
|
.test {
|
||||||
// 可以通过 @apply 多个样式封装整体样式
|
// 可以通过 @apply 多个样式封装整体样式
|
||||||
|
187
src/types/auto-import.d.ts
vendored
187
src/types/auto-import.d.ts
vendored
@ -1,187 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
// Generated by unplugin-auto-import
|
|
||||||
export {}
|
|
||||||
declare global {
|
|
||||||
const EffectScope: typeof import('vue')['EffectScope']
|
|
||||||
const computed: typeof import('vue')['computed']
|
|
||||||
const createApp: typeof import('vue')['createApp']
|
|
||||||
const customRef: typeof import('vue')['customRef']
|
|
||||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
|
||||||
const defineComponent: typeof import('vue')['defineComponent']
|
|
||||||
const effectScope: typeof import('vue')['effectScope']
|
|
||||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
|
||||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
|
||||||
const h: typeof import('vue')['h']
|
|
||||||
const inject: typeof import('vue')['inject']
|
|
||||||
const isProxy: typeof import('vue')['isProxy']
|
|
||||||
const isReactive: typeof import('vue')['isReactive']
|
|
||||||
const isReadonly: typeof import('vue')['isReadonly']
|
|
||||||
const isRef: typeof import('vue')['isRef']
|
|
||||||
const markRaw: typeof import('vue')['markRaw']
|
|
||||||
const nextTick: typeof import('vue')['nextTick']
|
|
||||||
const onActivated: typeof import('vue')['onActivated']
|
|
||||||
const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
|
|
||||||
const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress']
|
|
||||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
|
||||||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
|
||||||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
|
||||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
|
||||||
const onError: typeof import('@dcloudio/uni-app')['onError']
|
|
||||||
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
|
||||||
const onHide: typeof import('@dcloudio/uni-app')['onHide']
|
|
||||||
const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch']
|
|
||||||
const onLoad: typeof import('@dcloudio/uni-app')['onLoad']
|
|
||||||
const onMounted: typeof import('vue')['onMounted']
|
|
||||||
const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']
|
|
||||||
const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']
|
|
||||||
const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']
|
|
||||||
const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']
|
|
||||||
const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']
|
|
||||||
const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound']
|
|
||||||
const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll']
|
|
||||||
const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh']
|
|
||||||
const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom']
|
|
||||||
const onReady: typeof import('@dcloudio/uni-app')['onReady']
|
|
||||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
|
||||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
|
||||||
const onResize: typeof import('@dcloudio/uni-app')['onResize']
|
|
||||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
|
||||||
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
|
||||||
const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage']
|
|
||||||
const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline']
|
|
||||||
const onShow: typeof import('@dcloudio/uni-app')['onShow']
|
|
||||||
const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap']
|
|
||||||
const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange']
|
|
||||||
const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection']
|
|
||||||
const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
|
|
||||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
|
||||||
const onUpdated: typeof import('vue')['onUpdated']
|
|
||||||
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
|
|
||||||
const provide: typeof import('vue')['provide']
|
|
||||||
const reactive: typeof import('vue')['reactive']
|
|
||||||
const readonly: typeof import('vue')['readonly']
|
|
||||||
const ref: typeof import('vue')['ref']
|
|
||||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
|
||||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
|
||||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
|
||||||
const shallowRef: typeof import('vue')['shallowRef']
|
|
||||||
const toRaw: typeof import('vue')['toRaw']
|
|
||||||
const toRef: typeof import('vue')['toRef']
|
|
||||||
const toRefs: typeof import('vue')['toRefs']
|
|
||||||
const toValue: typeof import('vue')['toValue']
|
|
||||||
const triggerRef: typeof import('vue')['triggerRef']
|
|
||||||
const unref: typeof import('vue')['unref']
|
|
||||||
const useAttrs: typeof import('vue')['useAttrs']
|
|
||||||
const useCssModule: typeof import('vue')['useCssModule']
|
|
||||||
const useCssVars: typeof import('vue')['useCssVars']
|
|
||||||
const useId: typeof import('vue')['useId']
|
|
||||||
const useModel: typeof import('vue')['useModel']
|
|
||||||
const useNavbarWeixin: (typeof import('../hooks/useNavbarWeixin'))['default']
|
|
||||||
const useRequest: typeof import('../hooks/useRequest')['default']
|
|
||||||
const useSlots: typeof import('vue')['useSlots']
|
|
||||||
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
|
||||||
const useUpload: typeof import('../hooks/useUpload')['default']
|
|
||||||
const useUpload2: typeof import('../hooks/useUpload2')['default']
|
|
||||||
const watch: typeof import('vue')['watch']
|
|
||||||
const watchEffect: typeof import('vue')['watchEffect']
|
|
||||||
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
|
||||||
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
|
||||||
}
|
|
||||||
// for type re-export
|
|
||||||
declare global {
|
|
||||||
// @ts-ignore
|
|
||||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
|
||||||
import('vue')
|
|
||||||
}
|
|
||||||
// for vue template auto import
|
|
||||||
import { UnwrapRef } from 'vue'
|
|
||||||
declare module 'vue' {
|
|
||||||
interface GlobalComponents {}
|
|
||||||
interface ComponentCustomProperties {
|
|
||||||
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
|
||||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
|
||||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
|
||||||
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
|
||||||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
|
||||||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
|
||||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
|
||||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
|
||||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
|
||||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
|
||||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
|
||||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
|
||||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
|
||||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
|
||||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
|
||||||
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
|
||||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
|
||||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
|
||||||
readonly onAddToFavorites: UnwrapRef<typeof import('@dcloudio/uni-app')['onAddToFavorites']>
|
|
||||||
readonly onBackPress: UnwrapRef<typeof import('@dcloudio/uni-app')['onBackPress']>
|
|
||||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
|
||||||
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
|
||||||
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
|
||||||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
|
||||||
readonly onError: UnwrapRef<typeof import('@dcloudio/uni-app')['onError']>
|
|
||||||
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
|
||||||
readonly onHide: UnwrapRef<typeof import('@dcloudio/uni-app')['onHide']>
|
|
||||||
readonly onLaunch: UnwrapRef<typeof import('@dcloudio/uni-app')['onLaunch']>
|
|
||||||
readonly onLoad: UnwrapRef<typeof import('@dcloudio/uni-app')['onLoad']>
|
|
||||||
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
|
||||||
readonly onNavigationBarButtonTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']>
|
|
||||||
readonly onNavigationBarSearchInputChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']>
|
|
||||||
readonly onNavigationBarSearchInputClicked: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']>
|
|
||||||
readonly onNavigationBarSearchInputConfirmed: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']>
|
|
||||||
readonly onNavigationBarSearchInputFocusChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']>
|
|
||||||
readonly onPageNotFound: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageNotFound']>
|
|
||||||
readonly onPageScroll: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageScroll']>
|
|
||||||
readonly onPullDownRefresh: UnwrapRef<typeof import('@dcloudio/uni-app')['onPullDownRefresh']>
|
|
||||||
readonly onReachBottom: UnwrapRef<typeof import('@dcloudio/uni-app')['onReachBottom']>
|
|
||||||
readonly onReady: UnwrapRef<typeof import('@dcloudio/uni-app')['onReady']>
|
|
||||||
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
|
||||||
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
|
||||||
readonly onResize: UnwrapRef<typeof import('@dcloudio/uni-app')['onResize']>
|
|
||||||
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
|
||||||
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
|
||||||
readonly onShareAppMessage: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareAppMessage']>
|
|
||||||
readonly onShareTimeline: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareTimeline']>
|
|
||||||
readonly onShow: UnwrapRef<typeof import('@dcloudio/uni-app')['onShow']>
|
|
||||||
readonly onTabItemTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onTabItemTap']>
|
|
||||||
readonly onThemeChange: UnwrapRef<typeof import('@dcloudio/uni-app')['onThemeChange']>
|
|
||||||
readonly onUnhandledRejection: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnhandledRejection']>
|
|
||||||
readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']>
|
|
||||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
|
||||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
|
||||||
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
|
|
||||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
|
||||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
|
||||||
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
|
||||||
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
|
||||||
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
|
||||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
|
||||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
|
||||||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
|
||||||
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
|
||||||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
|
||||||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
|
||||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
|
||||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
|
||||||
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
|
||||||
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
|
||||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
|
||||||
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
|
||||||
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
|
||||||
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
|
||||||
readonly useRequest: UnwrapRef<typeof import('../hooks/useRequest')['default']>
|
|
||||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
|
||||||
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
|
|
||||||
readonly useUpload: UnwrapRef<typeof import('../hooks/useUpload')['default']>
|
|
||||||
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
|
||||||
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
|
||||||
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
|
||||||
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
|
||||||
}
|
|
||||||
}
|
|
23
src/types/global.d.ts
vendored
23
src/types/global.d.ts
vendored
@ -1,23 +0,0 @@
|
|||||||
declare const __UNI_PLATFORM__:
|
|
||||||
| 'h5'
|
|
||||||
| 'app'
|
|
||||||
| 'mp-alipay'
|
|
||||||
| 'mp-baidu'
|
|
||||||
| 'mp-jd'
|
|
||||||
| 'mp-kuaishou'
|
|
||||||
| 'mp-lark'
|
|
||||||
| 'mp-qq'
|
|
||||||
| 'mp-toutiao'
|
|
||||||
| 'mp-weixin'
|
|
||||||
| 'quickapp-webview'
|
|
||||||
| 'quickapp-webview-huawei'
|
|
||||||
| 'quickapp-webview-union'
|
|
||||||
|
|
||||||
declare const __VITE_APP_PROXY__: 'true' | 'false'
|
|
||||||
|
|
||||||
declare namespace JSX {
|
|
||||||
interface IntrinsicElements {
|
|
||||||
template: any
|
|
||||||
block: any
|
|
||||||
}
|
|
||||||
}
|
|
8
src/types/shims-uni.d.ts
vendored
8
src/types/shims-uni.d.ts
vendored
@ -1,8 +0,0 @@
|
|||||||
/// <reference types='@dcloudio/types' />
|
|
||||||
import 'vue'
|
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
|
||||||
type Hooks = App.AppInstance & Page.PageInstance
|
|
||||||
|
|
||||||
interface ComponentCustomOptions extends Hooks {}
|
|
||||||
}
|
|
23
src/types/uni-pages.d.ts
vendored
23
src/types/uni-pages.d.ts
vendored
@ -1,23 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
|
||||||
// Generated by vite-plugin-uni-pages
|
|
||||||
|
|
||||||
interface NavigateToOptions {
|
|
||||||
url: "/pages/index/index" |
|
|
||||||
"/pages/about/about";
|
|
||||||
}
|
|
||||||
interface RedirectToOptions extends NavigateToOptions {}
|
|
||||||
|
|
||||||
interface SwitchTabOptions {
|
|
||||||
url: "/pages/index/index" | "/pages/about/about"
|
|
||||||
}
|
|
||||||
|
|
||||||
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;
|
|
||||||
|
|
||||||
declare interface Uni {
|
|
||||||
navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void;
|
|
||||||
redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void;
|
|
||||||
switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void;
|
|
||||||
reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void;
|
|
||||||
}
|
|
6
src/typings.d.ts
vendored
6
src/typings.d.ts
vendored
@ -1,14 +1,14 @@
|
|||||||
// 全局要用的类型放到这里
|
// 全局要用的类型放到这里
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
type IResData<T> = {
|
interface IResData<T> {
|
||||||
code: number
|
code: number
|
||||||
msg: string
|
msg: string
|
||||||
data: T
|
data: T
|
||||||
}
|
}
|
||||||
|
|
||||||
// uni.uploadFile文件上传参数
|
// uni.uploadFile文件上传参数
|
||||||
type IUniUploadFileOptions = {
|
interface IUniUploadFileOptions {
|
||||||
file?: File
|
file?: File
|
||||||
files?: UniApp.UploadFileOptionFiles[]
|
files?: UniApp.UploadFileOptionFiles[]
|
||||||
filePath?: string
|
filePath?: string
|
||||||
@ -16,7 +16,7 @@ declare global {
|
|||||||
formData?: any
|
formData?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
type IUserInfo = {
|
interface IUserInfo {
|
||||||
nickname?: string
|
nickname?: string
|
||||||
avatar?: string
|
avatar?: string
|
||||||
/** 微信的 openid,非微信没有这个字段 */
|
/** 微信的 openid,非微信没有这个字段 */
|
||||||
|
@ -4,3 +4,12 @@ export enum TestEnum {
|
|||||||
A = '1',
|
A = '1',
|
||||||
B = '2',
|
B = '2',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uni.uploadFile文件上传参数
|
||||||
|
export interface IUniUploadFileOptions {
|
||||||
|
file?: File
|
||||||
|
files?: UniApp.UploadFileOptionFiles[]
|
||||||
|
filePath?: string
|
||||||
|
name?: string
|
||||||
|
formData?: any
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { CustomRequestOptions } from '@/interceptors/request'
|
import type { CustomRequestOptions } from '@/interceptors/request'
|
||||||
|
|
||||||
export const http = <T>(options: CustomRequestOptions) => {
|
export function http<T>(options: CustomRequestOptions) {
|
||||||
// 1. 返回 Promise 对象
|
// 1. 返回 Promise 对象
|
||||||
return new Promise<IResData<T>>((resolve, reject) => {
|
return new Promise<IResData<T>>((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
@ -15,18 +15,20 @@ export const http = <T>(options: CustomRequestOptions) => {
|
|||||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
// 2.1 提取核心数据 res.data
|
// 2.1 提取核心数据 res.data
|
||||||
resolve(res.data as IResData<T>)
|
resolve(res.data as IResData<T>)
|
||||||
} else if (res.statusCode === 401) {
|
}
|
||||||
|
else if (res.statusCode === 401) {
|
||||||
// 401错误 -> 清理用户信息,跳转到登录页
|
// 401错误 -> 清理用户信息,跳转到登录页
|
||||||
// userStore.clearUserInfo()
|
// userStore.clearUserInfo()
|
||||||
// uni.navigateTo({ url: '/pages/login/login' })
|
// uni.navigateTo({ url: '/pages/login/login' })
|
||||||
reject(res)
|
reject(res)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// 其他错误 -> 根据后端错误信息轻提示
|
// 其他错误 -> 根据后端错误信息轻提示
|
||||||
!options.hideErrorToast &&
|
!options.hideErrorToast
|
||||||
uni.showToast({
|
&& uni.showToast({
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
title: (res.data as IResData<T>).msg || '请求错误',
|
title: (res.data as IResData<T>).msg || '请求错误',
|
||||||
})
|
})
|
||||||
reject(res)
|
reject(res)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -49,16 +51,13 @@ export const http = <T>(options: CustomRequestOptions) => {
|
|||||||
* @param header 请求头,默认为json格式
|
* @param header 请求头,默认为json格式
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const httpGet = <T>(
|
export function httpGet<T>(url: string, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
||||||
url: string,
|
|
||||||
query?: Record<string, any>,
|
|
||||||
header?: Record<string, any>,
|
|
||||||
) => {
|
|
||||||
return http<T>({
|
return http<T>({
|
||||||
url,
|
url,
|
||||||
query,
|
query,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
header,
|
header,
|
||||||
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,20 +69,44 @@ export const httpGet = <T>(
|
|||||||
* @param header 请求头,默认为json格式
|
* @param header 请求头,默认为json格式
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const httpPost = <T>(
|
export function httpPost<T>(url: string, data?: Record<string, any>, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
||||||
url: string,
|
|
||||||
data?: Record<string, any>,
|
|
||||||
query?: Record<string, any>,
|
|
||||||
header?: Record<string, any>,
|
|
||||||
) => {
|
|
||||||
return http<T>({
|
return http<T>({
|
||||||
url,
|
url,
|
||||||
query,
|
query,
|
||||||
data,
|
data,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
header,
|
header,
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* PUT 请求
|
||||||
|
*/
|
||||||
|
export function httpPut<T>(url: string, data?: Record<string, any>, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
||||||
|
return http<T>({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
query,
|
||||||
|
method: 'PUT',
|
||||||
|
header,
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE 请求(无请求体,仅 query)
|
||||||
|
*/
|
||||||
|
export function httpDelete<T>(url: string, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
|
||||||
|
return http<T>({
|
||||||
|
url,
|
||||||
|
query,
|
||||||
|
method: 'DELETE',
|
||||||
|
header,
|
||||||
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
http.get = httpGet
|
http.get = httpGet
|
||||||
http.post = httpPost
|
http.post = httpPost
|
||||||
|
http.put = httpPut
|
||||||
|
http.delete = httpDelete
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import pagesConfig from '@/pages.json'
|
import { pages, subPackages } from '@/pages.json'
|
||||||
import { isMpWeixin } from './platform'
|
import { isMpWeixin } from './platform'
|
||||||
|
|
||||||
const { pages, subPackages, tabBar = { list: [] } } = { ...pagesConfig }
|
export function getLastPage() {
|
||||||
|
|
||||||
export const getLastPage = () => {
|
|
||||||
// getCurrentPages() 至少有1个元素,所以不再额外判断
|
// getCurrentPages() 至少有1个元素,所以不再额外判断
|
||||||
// const lastPage = getCurrentPages().at(-1)
|
// const lastPage = getCurrentPages().at(-1)
|
||||||
// 上面那个在低版本安卓中打包会报错,所以改用下面这个【虽然我加了 src/interceptions/prototype.ts,但依然报错】
|
// 上面那个在低版本安卓中打包会报错,所以改用下面这个【虽然我加了 src/interceptions/prototype.ts,但依然报错】
|
||||||
@ -11,24 +9,12 @@ export const getLastPage = () => {
|
|||||||
return pages[pages.length - 1]
|
return pages[pages.length - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 判断当前页面是否是 tabbar 页 */
|
|
||||||
export const getIsTabbar = () => {
|
|
||||||
try {
|
|
||||||
const lastPage = getLastPage()
|
|
||||||
const currPath = lastPage?.route
|
|
||||||
|
|
||||||
return Boolean(tabBar?.list?.some((item) => item.pagePath === currPath))
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前页面路由的 path 路径和 redirectPath 路径
|
* 获取当前页面路由的 path 路径和 redirectPath 路径
|
||||||
* path 如 '/pages/login/index'
|
* path 如 '/pages/login/index'
|
||||||
* redirectPath 如 '/pages/demo/base/route-interceptor'
|
* redirectPath 如 '/pages/demo/base/route-interceptor'
|
||||||
*/
|
*/
|
||||||
export const currRoute = () => {
|
export function currRoute() {
|
||||||
const lastPage = getLastPage()
|
const lastPage = getLastPage()
|
||||||
const currRoute = (lastPage as any).$page
|
const currRoute = (lastPage as any).$page
|
||||||
// console.log('lastPage.$page:', currRoute)
|
// console.log('lastPage.$page:', currRoute)
|
||||||
@ -43,7 +29,7 @@ export const currRoute = () => {
|
|||||||
return getUrlObj(fullPath)
|
return getUrlObj(fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ensureDecodeURIComponent = (url: string) => {
|
function ensureDecodeURIComponent(url: string) {
|
||||||
if (url.startsWith('%')) {
|
if (url.startsWith('%')) {
|
||||||
return ensureDecodeURIComponent(decodeURIComponent(url))
|
return ensureDecodeURIComponent(decodeURIComponent(url))
|
||||||
}
|
}
|
||||||
@ -54,7 +40,7 @@ const ensureDecodeURIComponent = (url: string) => {
|
|||||||
* 比如输入url: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor
|
* 比如输入url: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor
|
||||||
* 输出: {path: /pages/login/index, query: {redirect: /pages/demo/base/route-interceptor}}
|
* 输出: {path: /pages/login/index, query: {redirect: /pages/demo/base/route-interceptor}}
|
||||||
*/
|
*/
|
||||||
export const getUrlObj = (url: string) => {
|
export function getUrlObj(url: string) {
|
||||||
const [path, queryStr] = url.split('?')
|
const [path, queryStr] = url.split('?')
|
||||||
// console.log(path, queryStr)
|
// console.log(path, queryStr)
|
||||||
|
|
||||||
@ -77,16 +63,15 @@ export const getUrlObj = (url: string) => {
|
|||||||
* 这里设计得通用一点,可以传递 key 作为判断依据,默认是 needLogin, 与 route-block 配对使用
|
* 这里设计得通用一点,可以传递 key 作为判断依据,默认是 needLogin, 与 route-block 配对使用
|
||||||
* 如果没有传 key,则表示所有的 pages,如果传递了 key, 则表示通过 key 过滤
|
* 如果没有传 key,则表示所有的 pages,如果传递了 key, 则表示通过 key 过滤
|
||||||
*/
|
*/
|
||||||
export const getAllPages = (key = 'needLogin') => {
|
export function getAllPages(key = 'needLogin') {
|
||||||
// 这里处理主包
|
// 这里处理主包
|
||||||
const mainPages = [
|
const mainPages = pages
|
||||||
...pages
|
.filter(page => !key || page[key])
|
||||||
.filter((page) => !key || page[key])
|
.map(page => ({
|
||||||
.map((page) => ({
|
...page,
|
||||||
...page,
|
path: `/${page.path}`,
|
||||||
path: `/${page.path}`,
|
}))
|
||||||
})),
|
|
||||||
]
|
|
||||||
// 这里处理分包
|
// 这里处理分包
|
||||||
const subPages: any[] = []
|
const subPages: any[] = []
|
||||||
subPackages.forEach((subPageObj) => {
|
subPackages.forEach((subPageObj) => {
|
||||||
@ -94,7 +79,7 @@ export const getAllPages = (key = 'needLogin') => {
|
|||||||
const { root } = subPageObj
|
const { root } = subPageObj
|
||||||
|
|
||||||
subPageObj.pages
|
subPageObj.pages
|
||||||
.filter((page) => !key || page[key])
|
.filter(page => !key || page[key])
|
||||||
.forEach((page: { path: string } & Record<string, any>) => {
|
.forEach((page: { path: string } & Record<string, any>) => {
|
||||||
subPages.push({
|
subPages.push({
|
||||||
...page,
|
...page,
|
||||||
@ -111,18 +96,18 @@ export const getAllPages = (key = 'needLogin') => {
|
|||||||
* 得到所有的需要登录的 pages,包括主包和分包的
|
* 得到所有的需要登录的 pages,包括主包和分包的
|
||||||
* 只得到 path 数组
|
* 只得到 path 数组
|
||||||
*/
|
*/
|
||||||
export const getNeedLoginPages = (): string[] => getAllPages('needLogin').map((page) => page.path)
|
export const getNeedLoginPages = (): string[] => getAllPages('needLogin').map(page => page.path)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 得到所有的需要登录的 pages,包括主包和分包的
|
* 得到所有的需要登录的 pages,包括主包和分包的
|
||||||
* 只得到 path 数组
|
* 只得到 path 数组
|
||||||
*/
|
*/
|
||||||
export const needLoginPages: string[] = getAllPages('needLogin').map((page) => page.path)
|
export const needLoginPages: string[] = getAllPages('needLogin').map(page => page.path)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据微信小程序当前环境,判断应该获取的 baseUrl
|
* 根据微信小程序当前环境,判断应该获取的 baseUrl
|
||||||
*/
|
*/
|
||||||
export const getEnvBaseUrl = () => {
|
export function getEnvBaseUrl() {
|
||||||
// 请求基准地址
|
// 请求基准地址
|
||||||
let baseUrl = import.meta.env.VITE_SERVER_BASEURL
|
let baseUrl = import.meta.env.VITE_SERVER_BASEURL
|
||||||
|
|
||||||
@ -151,7 +136,7 @@ export const getEnvBaseUrl = () => {
|
|||||||
/**
|
/**
|
||||||
* 根据微信小程序当前环境,判断应该获取的 UPLOAD_BASEURL
|
* 根据微信小程序当前环境,判断应该获取的 UPLOAD_BASEURL
|
||||||
*/
|
*/
|
||||||
export const getEnvBaseUploadUrl = () => {
|
export function getEnvBaseUploadUrl() {
|
||||||
// 请求基准地址
|
// 请求基准地址
|
||||||
let baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL
|
let baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL
|
||||||
|
|
||||||
|
29
src/utils/queryString.ts
Normal file
29
src/utils/queryString.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 将对象序列化为URL查询字符串,用于替代第三方的 qs 库,节省宝贵的体积
|
||||||
|
* 支持基本类型值和数组,不支持嵌套对象
|
||||||
|
* @param obj 要序列化的对象
|
||||||
|
* @returns 序列化后的查询字符串
|
||||||
|
*/
|
||||||
|
export function stringifyQuery(obj: Record<string, any>): string {
|
||||||
|
if (!obj || typeof obj !== 'object' || Array.isArray(obj))
|
||||||
|
return ''
|
||||||
|
|
||||||
|
return Object.entries(obj)
|
||||||
|
.filter(([_, value]) => value !== undefined && value !== null)
|
||||||
|
.map(([key, value]) => {
|
||||||
|
// 对键进行编码
|
||||||
|
const encodedKey = encodeURIComponent(key)
|
||||||
|
|
||||||
|
// 处理数组类型
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value
|
||||||
|
.filter(item => item !== undefined && item !== null)
|
||||||
|
.map(item => `${encodedKey}=${encodeURIComponent(item)}`)
|
||||||
|
.join('&')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理基本类型
|
||||||
|
return `${encodedKey}=${encodeURIComponent(value)}`
|
||||||
|
})
|
||||||
|
.join('&')
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
import { CustomRequestOptions } from '@/interceptors/request'
|
import type { CustomRequestOptions } from '@/interceptors/request'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求方法: 主要是对 uni.request 的封装,去适配 openapi-ts-request 的 request 方法
|
* 请求方法: 主要是对 uni.request 的封装,去适配 openapi-ts-request 的 request 方法
|
||||||
* @param options 请求参数
|
* @param options 请求参数
|
||||||
* @returns 返回 Promise 对象
|
* @returns 返回 Promise 对象
|
||||||
*/
|
*/
|
||||||
const http = <T>(options: CustomRequestOptions) => {
|
function http<T>(options: CustomRequestOptions) {
|
||||||
// 1. 返回 Promise 对象
|
// 1. 返回 Promise 对象
|
||||||
return new Promise<T>((resolve, reject) => {
|
return new Promise<T>((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
@ -20,18 +20,20 @@ const http = <T>(options: CustomRequestOptions) => {
|
|||||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
// 2.1 提取核心数据 res.data
|
// 2.1 提取核心数据 res.data
|
||||||
resolve(res.data as T)
|
resolve(res.data as T)
|
||||||
} else if (res.statusCode === 401) {
|
}
|
||||||
|
else if (res.statusCode === 401) {
|
||||||
// 401错误 -> 清理用户信息,跳转到登录页
|
// 401错误 -> 清理用户信息,跳转到登录页
|
||||||
// userStore.clearUserInfo()
|
// userStore.clearUserInfo()
|
||||||
// uni.navigateTo({ url: '/pages/login/login' })
|
// uni.navigateTo({ url: '/pages/login/login' })
|
||||||
reject(res)
|
reject(res)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// 其他错误 -> 根据后端错误信息轻提示
|
// 其他错误 -> 根据后端错误信息轻提示
|
||||||
!options.hideErrorToast &&
|
!options.hideErrorToast
|
||||||
uni.showToast({
|
&& uni.showToast({
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
title: (res.data as T & { msg?: string })?.msg || '请求错误',
|
title: (res.data as T & { msg?: string })?.msg || '请求错误',
|
||||||
})
|
})
|
||||||
reject(res)
|
reject(res)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
65
src/utils/toast.ts
Normal file
65
src/utils/toast.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* toast 弹窗组件
|
||||||
|
* 支持 success/error/warning/info 四种状态
|
||||||
|
* 可配置 duration, position 等参数
|
||||||
|
*/
|
||||||
|
|
||||||
|
type ToastType = 'success' | 'error' | 'warning' | 'info'
|
||||||
|
|
||||||
|
interface ToastOptions {
|
||||||
|
type?: ToastType
|
||||||
|
duration?: number
|
||||||
|
position?: 'top' | 'middle' | 'bottom'
|
||||||
|
icon?: 'success' | 'error' | 'none' | 'loading' | 'fail' | 'exception'
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showToast(options: ToastOptions | string) {
|
||||||
|
const defaultOptions: ToastOptions = {
|
||||||
|
type: 'info',
|
||||||
|
duration: 2000,
|
||||||
|
position: 'middle',
|
||||||
|
message: '',
|
||||||
|
}
|
||||||
|
const mergedOptions
|
||||||
|
= typeof options === 'string'
|
||||||
|
? { ...defaultOptions, message: options }
|
||||||
|
: { ...defaultOptions, ...options }
|
||||||
|
// 映射position到uniapp支持的格式
|
||||||
|
const positionMap: Record<ToastOptions['position'], 'top' | 'bottom' | 'center'> = {
|
||||||
|
top: 'top',
|
||||||
|
middle: 'center',
|
||||||
|
bottom: 'bottom',
|
||||||
|
}
|
||||||
|
|
||||||
|
// 映射图标类型
|
||||||
|
const iconMap: Record<
|
||||||
|
ToastType,
|
||||||
|
'success' | 'error' | 'none' | 'loading' | 'fail' | 'exception'
|
||||||
|
> = {
|
||||||
|
success: 'success',
|
||||||
|
error: 'error',
|
||||||
|
warning: 'fail',
|
||||||
|
info: 'none',
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用uni.showToast显示提示
|
||||||
|
uni.showToast({
|
||||||
|
title: mergedOptions.message,
|
||||||
|
duration: mergedOptions.duration,
|
||||||
|
position: positionMap[mergedOptions.position],
|
||||||
|
icon: mergedOptions.icon || iconMap[mergedOptions.type],
|
||||||
|
mask: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toast = {
|
||||||
|
success: (message: string, options?: Omit<ToastOptions, 'type'>) =>
|
||||||
|
showToast({ ...options, type: 'success', message }),
|
||||||
|
error: (message: string, options?: Omit<ToastOptions, 'type'>) =>
|
||||||
|
showToast({ ...options, type: 'error', message }),
|
||||||
|
warning: (message: string, options?: Omit<ToastOptions, 'type'>) =>
|
||||||
|
showToast({ ...options, type: 'warning', message }),
|
||||||
|
info: (message: string, options?: Omit<ToastOptions, 'type'>) =>
|
||||||
|
showToast({ ...options, type: 'info', message }),
|
||||||
|
}
|
324
src/utils/uploadFile.ts
Normal file
324
src/utils/uploadFile.ts
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
import { toast } from './toast'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传钩子函数使用示例
|
||||||
|
* @example
|
||||||
|
* const { loading, error, data, progress, run } = useUpload<IUploadResult>(
|
||||||
|
* uploadUrl,
|
||||||
|
* {},
|
||||||
|
* {
|
||||||
|
* maxSize: 5, // 最大5MB
|
||||||
|
* sourceType: ['album'], // 仅支持从相册选择
|
||||||
|
* onProgress: (p) => console.log(`上传进度:${p}%`),
|
||||||
|
* onSuccess: (res) => console.log('上传成功', res),
|
||||||
|
* onError: (err) => console.error('上传失败', err),
|
||||||
|
* },
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件的URL配置
|
||||||
|
*/
|
||||||
|
export const uploadFileUrl = {
|
||||||
|
/** 用户头像上传地址 */
|
||||||
|
USER_AVATAR: `${import.meta.env.VITE_SERVER_BASEURL}/user/avatar`,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用文件上传函数(支持直接传入文件路径)
|
||||||
|
* @param url 上传地址
|
||||||
|
* @param filePath 本地文件路径
|
||||||
|
* @param formData 额外表单数据
|
||||||
|
* @param options 上传选项
|
||||||
|
*/
|
||||||
|
export function useFileUpload<T = string>(url: string, filePath: string, formData: Record<string, any> = {}, options: Omit<UploadOptions, 'sourceType' | 'sizeType' | 'count'> = {}) {
|
||||||
|
return useUpload<T>(
|
||||||
|
url,
|
||||||
|
formData,
|
||||||
|
{
|
||||||
|
...options,
|
||||||
|
sourceType: ['album'],
|
||||||
|
sizeType: ['original'],
|
||||||
|
},
|
||||||
|
filePath,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UploadOptions {
|
||||||
|
/** 最大可选择的图片数量,默认为1 */
|
||||||
|
count?: number
|
||||||
|
/** 所选的图片的尺寸,original-原图,compressed-压缩图 */
|
||||||
|
sizeType?: Array<'original' | 'compressed'>
|
||||||
|
/** 选择图片的来源,album-相册,camera-相机 */
|
||||||
|
sourceType?: Array<'album' | 'camera'>
|
||||||
|
/** 文件大小限制,单位:MB */
|
||||||
|
maxSize?: number //
|
||||||
|
/** 上传进度回调函数 */
|
||||||
|
onProgress?: (progress: number) => void
|
||||||
|
/** 上传成功回调函数 */
|
||||||
|
onSuccess?: (res: Record<string, any>) => void
|
||||||
|
/** 上传失败回调函数 */
|
||||||
|
onError?: (err: Error | UniApp.GeneralCallbackResult) => void
|
||||||
|
/** 上传完成回调函数(无论成功失败) */
|
||||||
|
onComplete?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传钩子函数
|
||||||
|
* @template T 上传成功后返回的数据类型
|
||||||
|
* @param url 上传地址
|
||||||
|
* @param formData 额外的表单数据
|
||||||
|
* @param options 上传选项
|
||||||
|
* @returns 上传状态和控制对象
|
||||||
|
*/
|
||||||
|
export function useUpload<T = string>(url: string, formData: Record<string, any> = {}, options: UploadOptions = {},
|
||||||
|
/** 直接传入文件路径,跳过选择器 */
|
||||||
|
directFilePath?: string) {
|
||||||
|
/** 上传中状态 */
|
||||||
|
const loading = ref(false)
|
||||||
|
/** 上传错误状态 */
|
||||||
|
const error = ref(false)
|
||||||
|
/** 上传成功后的响应数据 */
|
||||||
|
const data = ref<T>()
|
||||||
|
/** 上传进度(0-100) */
|
||||||
|
const progress = ref(0)
|
||||||
|
|
||||||
|
/** 解构上传选项,设置默认值 */
|
||||||
|
const {
|
||||||
|
/** 最大可选择的图片数量 */
|
||||||
|
count = 1,
|
||||||
|
/** 所选的图片的尺寸 */
|
||||||
|
sizeType = ['original', 'compressed'],
|
||||||
|
/** 选择图片的来源 */
|
||||||
|
sourceType = ['album', 'camera'],
|
||||||
|
/** 文件大小限制(MB) */
|
||||||
|
maxSize = 10,
|
||||||
|
/** 进度回调 */
|
||||||
|
onProgress,
|
||||||
|
/** 成功回调 */
|
||||||
|
onSuccess,
|
||||||
|
/** 失败回调 */
|
||||||
|
onError,
|
||||||
|
/** 完成回调 */
|
||||||
|
onComplete,
|
||||||
|
} = options
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件大小是否超过限制
|
||||||
|
* @param size 文件大小(字节)
|
||||||
|
* @returns 是否通过检查
|
||||||
|
*/
|
||||||
|
const checkFileSize = (size: number) => {
|
||||||
|
const sizeInMB = size / 1024 / 1024
|
||||||
|
if (sizeInMB > maxSize) {
|
||||||
|
toast.warning(`文件大小不能超过${maxSize}MB`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 触发文件选择和上传
|
||||||
|
* 根据平台使用不同的选择器:
|
||||||
|
* - 微信小程序使用 chooseMedia
|
||||||
|
* - 其他平台使用 chooseImage
|
||||||
|
*/
|
||||||
|
const run = () => {
|
||||||
|
if (directFilePath) {
|
||||||
|
// 直接使用传入的文件路径
|
||||||
|
loading.value = true
|
||||||
|
progress.value = 0
|
||||||
|
uploadFile<T>({
|
||||||
|
url,
|
||||||
|
tempFilePath: directFilePath,
|
||||||
|
formData,
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
progress,
|
||||||
|
onProgress,
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
onComplete,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 微信小程序环境下使用 chooseMedia API
|
||||||
|
uni.chooseMedia({
|
||||||
|
count,
|
||||||
|
mediaType: ['image'], // 仅支持图片类型
|
||||||
|
sourceType,
|
||||||
|
success: (res) => {
|
||||||
|
const file = res.tempFiles[0]
|
||||||
|
// 检查文件大小是否符合限制
|
||||||
|
if (!checkFileSize(file.size))
|
||||||
|
return
|
||||||
|
|
||||||
|
// 开始上传
|
||||||
|
loading.value = true
|
||||||
|
progress.value = 0
|
||||||
|
uploadFile<T>({
|
||||||
|
url,
|
||||||
|
tempFilePath: file.tempFilePath,
|
||||||
|
formData,
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
progress,
|
||||||
|
onProgress,
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
onComplete,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('选择媒体文件失败:', err)
|
||||||
|
error.value = true
|
||||||
|
onError?.(err)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
// 非微信小程序环境下使用 chooseImage API
|
||||||
|
uni.chooseImage({
|
||||||
|
count,
|
||||||
|
sizeType,
|
||||||
|
sourceType,
|
||||||
|
success: (res) => {
|
||||||
|
console.log('选择图片成功:', res)
|
||||||
|
|
||||||
|
// 开始上传
|
||||||
|
loading.value = true
|
||||||
|
progress.value = 0
|
||||||
|
uploadFile<T>({
|
||||||
|
url,
|
||||||
|
tempFilePath: res.tempFilePaths[0],
|
||||||
|
formData,
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
progress,
|
||||||
|
onProgress,
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
onComplete,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('选择图片失败:', err)
|
||||||
|
error.value = true
|
||||||
|
onError?.(err)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return { loading, error, data, progress, run }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传选项接口
|
||||||
|
* @template T 上传成功后返回的数据类型
|
||||||
|
*/
|
||||||
|
interface UploadFileOptions<T> {
|
||||||
|
/** 上传地址 */
|
||||||
|
url: string
|
||||||
|
/** 临时文件路径 */
|
||||||
|
tempFilePath: string
|
||||||
|
/** 额外的表单数据 */
|
||||||
|
formData: Record<string, any>
|
||||||
|
/** 上传成功后的响应数据 */
|
||||||
|
data: Ref<T | undefined>
|
||||||
|
/** 上传错误状态 */
|
||||||
|
error: Ref<boolean>
|
||||||
|
/** 上传中状态 */
|
||||||
|
loading: Ref<boolean>
|
||||||
|
/** 上传进度(0-100) */
|
||||||
|
progress: Ref<number>
|
||||||
|
/** 上传进度回调 */
|
||||||
|
onProgress?: (progress: number) => void
|
||||||
|
/** 上传成功回调 */
|
||||||
|
onSuccess?: (res: Record<string, any>) => void
|
||||||
|
/** 上传失败回调 */
|
||||||
|
onError?: (err: Error | UniApp.GeneralCallbackResult) => void
|
||||||
|
/** 上传完成回调 */
|
||||||
|
onComplete?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行文件上传
|
||||||
|
* @template T 上传成功后返回的数据类型
|
||||||
|
* @param options 上传选项
|
||||||
|
*/
|
||||||
|
function uploadFile<T>({
|
||||||
|
url,
|
||||||
|
tempFilePath,
|
||||||
|
formData,
|
||||||
|
data,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
progress,
|
||||||
|
onProgress,
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
onComplete,
|
||||||
|
}: UploadFileOptions<T>) {
|
||||||
|
try {
|
||||||
|
// 创建上传任务
|
||||||
|
const uploadTask = uni.uploadFile({
|
||||||
|
url,
|
||||||
|
filePath: tempFilePath,
|
||||||
|
name: 'file', // 文件对应的 key
|
||||||
|
formData,
|
||||||
|
header: {
|
||||||
|
// H5环境下不需要手动设置Content-Type,让浏览器自动处理multipart格式
|
||||||
|
// #ifndef H5
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
// 确保文件名称合法
|
||||||
|
success: (uploadFileRes) => {
|
||||||
|
console.log('上传文件成功:', uploadFileRes)
|
||||||
|
try {
|
||||||
|
// 解析响应数据
|
||||||
|
const { data: _data } = JSON.parse(uploadFileRes.data)
|
||||||
|
// 上传成功
|
||||||
|
data.value = _data as T
|
||||||
|
onSuccess?.(_data)
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
// 响应解析错误
|
||||||
|
console.error('解析上传响应失败:', err)
|
||||||
|
error.value = true
|
||||||
|
onError?.(new Error('上传响应解析失败'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
// 上传请求失败
|
||||||
|
console.error('上传文件失败:', err)
|
||||||
|
error.value = true
|
||||||
|
onError?.(err)
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
// 无论成功失败都执行
|
||||||
|
loading.value = false
|
||||||
|
onComplete?.()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听上传进度
|
||||||
|
uploadTask.onProgressUpdate((res) => {
|
||||||
|
progress.value = res.progress
|
||||||
|
onProgress?.(res.progress)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
// 创建上传任务失败
|
||||||
|
console.error('创建上传任务失败:', err)
|
||||||
|
error.value = true
|
||||||
|
loading.value = false
|
||||||
|
onError?.(new Error('创建上传任务失败'))
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,15 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"skipLibCheck": true,
|
"lib": ["esnext", "dom"],
|
||||||
|
"baseUrl": ".",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"resolveJsonModule": true,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"allowJs": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"],
|
||||||
|
"@img/*": ["./src/static/*"]
|
||||||
},
|
},
|
||||||
"outDir": "dist",
|
"resolveJsonModule": true,
|
||||||
"lib": ["esnext", "dom"],
|
|
||||||
"types": [
|
"types": [
|
||||||
"@dcloudio/types",
|
"@dcloudio/types",
|
||||||
"@uni-helper/uni-types",
|
"@uni-helper/uni-types",
|
||||||
@ -22,12 +17,17 @@
|
|||||||
"wot-design-uni/global.d.ts",
|
"wot-design-uni/global.d.ts",
|
||||||
"z-paging/types",
|
"z-paging/types",
|
||||||
"./src/typings.d.ts"
|
"./src/typings.d.ts"
|
||||||
]
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"vueCompilerOptions": {
|
"vueCompilerOptions": {
|
||||||
"plugins": ["@uni-helper/uni-types/volar-plugin"]
|
"plugins": ["@uni-helper/uni-types/volar-plugin"]
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules"],
|
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
"src/**/*.js",
|
"src/**/*.js",
|
||||||
@ -36,5 +36,6 @@
|
|||||||
"src/**/*.jsx",
|
"src/**/*.jsx",
|
||||||
"src/**/*.vue",
|
"src/**/*.vue",
|
||||||
"src/**/*.json"
|
"src/**/*.json"
|
||||||
]
|
],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
@ -1,71 +1,45 @@
|
|||||||
// uno.config.ts
|
// https://www.npmjs.com/package/@uni-helper/unocss-preset-uni
|
||||||
|
import { presetUni } from '@uni-helper/unocss-preset-uni'
|
||||||
import {
|
import {
|
||||||
type Preset,
|
|
||||||
defineConfig,
|
defineConfig,
|
||||||
presetUno,
|
|
||||||
presetAttributify,
|
presetAttributify,
|
||||||
presetIcons,
|
presetIcons,
|
||||||
transformerDirectives,
|
transformerDirectives,
|
||||||
transformerVariantGroup,
|
transformerVariantGroup,
|
||||||
} from 'unocss'
|
} from 'unocss'
|
||||||
|
|
||||||
import { presetApplet, presetRemRpx, transformerAttributify } from 'unocss-applet'
|
|
||||||
|
|
||||||
// @see https://unocss.dev/presets/legacy-compat
|
|
||||||
// import { presetLegacyCompat } from '@unocss/preset-legacy-compat'
|
|
||||||
|
|
||||||
const isMp = process.env?.UNI_PLATFORM?.startsWith('mp') ?? false
|
|
||||||
|
|
||||||
const presets: Preset[] = []
|
|
||||||
if (isMp) {
|
|
||||||
// 使用小程序预设
|
|
||||||
presets.push(presetApplet(), presetRemRpx())
|
|
||||||
} else {
|
|
||||||
presets.push(
|
|
||||||
// 非小程序用官方预设
|
|
||||||
presetUno(),
|
|
||||||
// 支持css class属性化
|
|
||||||
presetAttributify(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
presets: [
|
presets: [
|
||||||
...presets,
|
presetUni({
|
||||||
// 支持图标,需要搭配图标库,eg: @iconify-json/carbon, 使用 `<button class="i-carbon-sun dark:i-carbon-moon" />`
|
attributify: {
|
||||||
|
// prefix: 'fg-', // 如果加前缀,则需要在代码里面使用 `fg-` 前缀,如:<div fg-border="1px solid #000"></div>
|
||||||
|
prefixedOnly: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
presetIcons({
|
presetIcons({
|
||||||
scale: 1.2,
|
scale: 1.2,
|
||||||
warn: true,
|
warn: true,
|
||||||
extraProperties: {
|
extraProperties: {
|
||||||
display: 'inline-block',
|
'display': 'inline-block',
|
||||||
'vertical-align': 'middle',
|
'vertical-align': 'middle',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
// 将颜色函数 (rgb()和hsl()) 从空格分隔转换为逗号分隔,更好的兼容性app端,example:
|
// 支持css class属性化
|
||||||
// `rgb(255 0 0)` -> `rgb(255, 0, 0)`
|
presetAttributify(),
|
||||||
// `rgba(255 0 0 / 0.5)` -> `rgba(255, 0, 0, 0.5)`
|
|
||||||
// 与群友的正常写法冲突,先去掉!(2024-05-25)
|
|
||||||
// presetLegacyCompat({
|
|
||||||
// commaStyleColorFunction: true,
|
|
||||||
// }) as Preset,
|
|
||||||
],
|
],
|
||||||
/**
|
|
||||||
* 自定义快捷语句
|
|
||||||
* @see https://github.com/unocss/unocss#shortcuts
|
|
||||||
*/
|
|
||||||
shortcuts: [['center', 'flex justify-center items-center']],
|
|
||||||
transformers: [
|
transformers: [
|
||||||
// 启用 @apply 功能
|
// 启用指令功能:主要用于支持 @apply、@screen 和 theme() 等 CSS 指令
|
||||||
transformerDirectives(),
|
transformerDirectives(),
|
||||||
// 启用 () 分组功能
|
// 启用 () 分组功能
|
||||||
// 支持css class组合,eg: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">测试 unocss</div>`
|
// 支持css class组合,eg: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">测试 unocss</div>`
|
||||||
transformerVariantGroup(),
|
transformerVariantGroup(),
|
||||||
// Don't change the following order
|
|
||||||
transformerAttributify({
|
|
||||||
// 解决与第三方框架样式冲突问题
|
|
||||||
prefixedOnly: true,
|
|
||||||
prefix: 'fg',
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
|
shortcuts: [
|
||||||
|
{
|
||||||
|
center: 'flex justify-center items-center',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
safelist: [],
|
||||||
rules: [
|
rules: [
|
||||||
[
|
[
|
||||||
'p-safe',
|
'p-safe',
|
||||||
@ -77,13 +51,15 @@ export default defineConfig({
|
|||||||
['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
|
['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
|
||||||
['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
|
['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
|
||||||
],
|
],
|
||||||
|
theme: {
|
||||||
|
colors: {
|
||||||
|
/** 主题色,用法如: text-primary */
|
||||||
|
primary: 'var(--wot-color-theme,#0957DE)',
|
||||||
|
},
|
||||||
|
fontSize: {
|
||||||
|
/** 提供更小号的字体,用法如:text-2xs */
|
||||||
|
'2xs': ['20rpx', '28rpx'],
|
||||||
|
'3xs': ['18rpx', '26rpx'],
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* 最终这一套组合下来会得到:
|
|
||||||
* mp 里面:mt-4 => margin-top: 32rpx == 16px
|
|
||||||
* h5 里面:mt-4 => margin-top: 1rem == 16px
|
|
||||||
*
|
|
||||||
* 如果是传统方式写样式,则推荐设计稿设置为 750,这样设计稿1px,代码写1rpx。
|
|
||||||
* rpx是响应式的,可以让不同设备的屏幕显示效果保持一致。
|
|
||||||
*/
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import path from 'node:path'
|
||||||
|
import process from 'node:process'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
export function copyNativeRes() {
|
export function copyNativeRes() {
|
||||||
const waitPath = path.resolve(__dirname, '../src/nativeResources')
|
const waitPath = path.resolve(__dirname, '../src/nativeResources')
|
||||||
@ -31,7 +32,8 @@ export function copyNativeRes() {
|
|||||||
console.log(
|
console.log(
|
||||||
`[copyNativeRes] 成功将 nativeResources 目录中的资源移动到构建目录:${buildPath}`,
|
`[copyNativeRes] 成功将 nativeResources 目录中的资源移动到构建目录:${buildPath}`,
|
||||||
)
|
)
|
||||||
} catch (error) {
|
}
|
||||||
|
catch (error) {
|
||||||
console.error(`[copyNativeRes] 复制资源失败:`, error)
|
console.error(`[copyNativeRes] 复制资源失败:`, error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
37
vite-plugins/updatePackageJson.ts
Normal file
37
vite-plugins/updatePackageJson.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// src/plugins/updatePackageJson.ts
|
||||||
|
import type { Plugin } from 'vite'
|
||||||
|
import fs from 'node:fs/promises'
|
||||||
|
import path from 'node:path'
|
||||||
|
import process from 'node:process'
|
||||||
|
|
||||||
|
function updatePackageJson(): Plugin {
|
||||||
|
return {
|
||||||
|
name: 'update-package-json',
|
||||||
|
async buildStart() {
|
||||||
|
// 只在生产环境构建时执行
|
||||||
|
if (process.env.NODE_ENV !== 'production')
|
||||||
|
return
|
||||||
|
|
||||||
|
const packageJsonPath = path.resolve(process.cwd(), 'package.json')
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 读取并解析 package.json
|
||||||
|
const content = await fs.readFile(packageJsonPath, 'utf-8')
|
||||||
|
const packageJson = JSON.parse(content)
|
||||||
|
|
||||||
|
// 更新时间戳(使用 ISO 格式或自定义格式)
|
||||||
|
packageJson['update-time'] = new Date().toISOString().split('T')[0] // YYYY-MM-DD
|
||||||
|
|
||||||
|
// 写回文件(保持 2 空格缩进)
|
||||||
|
await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf-8')
|
||||||
|
|
||||||
|
console.log(`[update-package-json] 更新时间戳: ${packageJson['update-time']}`)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('[update-package-json] 插件执行失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default updatePackageJson
|
@ -1,25 +1,32 @@
|
|||||||
import Uni from '@dcloudio/vite-plugin-uni'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { defineConfig, loadEnv } from 'vite'
|
import process from 'node:process'
|
||||||
// @see https://uni-helper.js.org/vite-plugin-uni-pages
|
import Uni from '@dcloudio/vite-plugin-uni'
|
||||||
import UniPages from '@uni-helper/vite-plugin-uni-pages'
|
import Components from '@uni-helper/vite-plugin-uni-components'
|
||||||
// @see https://uni-helper.js.org/vite-plugin-uni-layouts
|
// @see https://uni-helper.js.org/vite-plugin-uni-layouts
|
||||||
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
|
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
|
||||||
|
// @see https://github.com/uni-helper/vite-plugin-uni-manifest
|
||||||
|
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
|
||||||
|
// @see https://uni-helper.js.org/vite-plugin-uni-pages
|
||||||
|
import UniPages from '@uni-helper/vite-plugin-uni-pages'
|
||||||
// @see https://github.com/uni-helper/vite-plugin-uni-platform
|
// @see https://github.com/uni-helper/vite-plugin-uni-platform
|
||||||
// 需要与 @uni-helper/vite-plugin-uni-pages 插件一起使用
|
// 需要与 @uni-helper/vite-plugin-uni-pages 插件一起使用
|
||||||
import UniPlatform from '@uni-helper/vite-plugin-uni-platform'
|
import UniPlatform from '@uni-helper/vite-plugin-uni-platform'
|
||||||
// @see https://github.com/uni-helper/vite-plugin-uni-manifest
|
/**
|
||||||
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
|
* 分包优化、模块异步跨包调用、组件异步跨包引用
|
||||||
// @see https://unocss.dev/
|
* @see https://github.com/uni-ku/bundle-optimizer
|
||||||
|
*/
|
||||||
|
import Optimization from '@uni-ku/bundle-optimizer'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
import UnoCSS from 'unocss/vite'
|
|
||||||
import AutoImport from 'unplugin-auto-import/vite'
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
|
import { defineConfig, loadEnv } from 'vite'
|
||||||
import ViteRestart from 'vite-plugin-restart'
|
import ViteRestart from 'vite-plugin-restart'
|
||||||
import { copyNativeRes } from './vite-plugins/copyNativeRes'
|
import updatePackageJson from './vite-plugins/updatePackageJson'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default ({ command, mode }) => {
|
export default async ({ command, mode }) => {
|
||||||
|
// @see https://unocss.dev/
|
||||||
|
const UnoCSS = (await import('unocss/vite')).default
|
||||||
// console.log(mode === process.env.NODE_ENV) // true
|
// console.log(mode === process.env.NODE_ENV) // true
|
||||||
|
|
||||||
// mode: 区分生产环境还是开发环境
|
// mode: 区分生产环境还是开发环境
|
||||||
@ -62,14 +69,13 @@ export default ({ command, mode }) => {
|
|||||||
UniPlatform(),
|
UniPlatform(),
|
||||||
UniManifest(),
|
UniManifest(),
|
||||||
// UniXXX 需要在 Uni 之前引入
|
// UniXXX 需要在 Uni 之前引入
|
||||||
Uni(),
|
|
||||||
{
|
{
|
||||||
// 临时解决 dcloudio 官方的 @dcloudio/uni-mp-compiler 出现的编译 BUG
|
// 临时解决 dcloudio 官方的 @dcloudio/uni-mp-compiler 出现的编译 BUG
|
||||||
// 参考 github issue: https://github.com/dcloudio/uni-app/issues/4952
|
// 参考 github issue: https://github.com/dcloudio/uni-app/issues/4952
|
||||||
// 自定义插件禁用 vite:vue 插件的 devToolsEnabled,强制编译 vue 模板时 inline 为 true
|
// 自定义插件禁用 vite:vue 插件的 devToolsEnabled,强制编译 vue 模板时 inline 为 true
|
||||||
name: 'fix-vite-plugin-vue',
|
name: 'fix-vite-plugin-vue',
|
||||||
configResolved(config) {
|
configResolved(config) {
|
||||||
const plugin = config.plugins.find((p) => p.name === 'vite:vue')
|
const plugin = config.plugins.find(p => p.name === 'vite:vue')
|
||||||
if (plugin && plugin.api && plugin.api.options) {
|
if (plugin && plugin.api && plugin.api.options) {
|
||||||
plugin.api.options.devToolsEnabled = false
|
plugin.api.options.devToolsEnabled = false
|
||||||
}
|
}
|
||||||
@ -80,9 +86,20 @@ export default ({ command, mode }) => {
|
|||||||
imports: ['vue', 'uni-app'],
|
imports: ['vue', 'uni-app'],
|
||||||
dts: 'src/types/auto-import.d.ts',
|
dts: 'src/types/auto-import.d.ts',
|
||||||
dirs: ['src/hooks'], // 自动导入 hooks
|
dirs: ['src/hooks'], // 自动导入 hooks
|
||||||
eslintrc: { enabled: true },
|
|
||||||
vueTemplate: true, // default false
|
vueTemplate: true, // default false
|
||||||
}),
|
}),
|
||||||
|
// Optimization 插件需要 page.json 文件,故应在 UniPages 插件之后执行
|
||||||
|
Optimization({
|
||||||
|
enable: {
|
||||||
|
'optimization': true,
|
||||||
|
'async-import': true,
|
||||||
|
'async-component': true,
|
||||||
|
},
|
||||||
|
dts: {
|
||||||
|
base: 'src/types',
|
||||||
|
},
|
||||||
|
logger: false,
|
||||||
|
}),
|
||||||
|
|
||||||
ViteRestart({
|
ViteRestart({
|
||||||
// 通过这个插件,在修改vite.config.js文件则不需要重新运行也生效配置
|
// 通过这个插件,在修改vite.config.js文件则不需要重新运行也生效配置
|
||||||
@ -96,16 +113,24 @@ export default ({ command, mode }) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
// 打包分析插件,h5 + 生产环境才弹出
|
// 打包分析插件,h5 + 生产环境才弹出
|
||||||
UNI_PLATFORM === 'h5' &&
|
UNI_PLATFORM === 'h5'
|
||||||
mode === 'production' &&
|
&& mode === 'production'
|
||||||
visualizer({
|
&& visualizer({
|
||||||
filename: './node_modules/.cache/visualizer/stats.html',
|
filename: './node_modules/.cache/visualizer/stats.html',
|
||||||
open: true,
|
open: true,
|
||||||
gzipSize: true,
|
gzipSize: true,
|
||||||
brotliSize: true,
|
brotliSize: true,
|
||||||
}),
|
}),
|
||||||
// 只有在 app 平台时才启用 copyNativeRes 插件
|
// 只有在 app 平台时才启用 copyNativeRes 插件
|
||||||
UNI_PLATFORM === 'app' && copyNativeRes(),
|
// UNI_PLATFORM === 'app' && copyNativeRes(),
|
||||||
|
Components({
|
||||||
|
extensions: ['vue'],
|
||||||
|
deep: true, // 是否递归扫描子目录,
|
||||||
|
directoryAsNamespace: false, // 是否把目录名作为命名空间前缀,true 时组件名为 目录名+组件名,
|
||||||
|
dts: 'src/types/components.d.ts', // 自动生成的组件类型声明文件路径(用于 TypeScript 支持)
|
||||||
|
}),
|
||||||
|
Uni(),
|
||||||
|
updatePackageJson(),
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
__UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
|
__UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
|
||||||
@ -138,14 +163,15 @@ export default ({ command, mode }) => {
|
|||||||
[VITE_APP_PROXY_PREFIX]: {
|
[VITE_APP_PROXY_PREFIX]: {
|
||||||
target: VITE_SERVER_BASEURL,
|
target: VITE_SERVER_BASEURL,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
rewrite: (path) => path.replace(new RegExp(`^${VITE_APP_PROXY_PREFIX}`), ''),
|
rewrite: path => path.replace(new RegExp(`^${VITE_APP_PROXY_PREFIX}`), ''),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
|
sourcemap: false,
|
||||||
// 方便非h5端调试
|
// 方便非h5端调试
|
||||||
sourcemap: VITE_SHOW_SOURCEMAP === 'true', // 默认是false
|
// sourcemap: VITE_SHOW_SOURCEMAP === 'true', // 默认是false
|
||||||
target: 'es6',
|
target: 'es6',
|
||||||
// 开发环境不用压缩
|
// 开发环境不用压缩
|
||||||
minify: mode === 'development' ? false : 'terser',
|
minify: mode === 'development' ? false : 'terser',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user