Compare commits

...

213 Commits

Author SHA1 Message Date
chenjiahan
b45b0b7608 chore: update branches in GitHub actions 2022-09-10 21:57:09 +08:00
chenjiahan
c309ca2aaf docs: switch dev branch to v4 2022-09-10 21:53:49 +08:00
chenjiahan
66df033ec8 Merge branch 'dev' into next 2022-09-10 21:51:26 +08:00
neverland
e2ea3be819
feat: export props of all components (#11024) 2022-09-10 21:49:21 +08:00
neverland
019c328975
chore: remove *.less from sideEffects (#11023) 2022-09-10 17:17:23 +08:00
neverland
9418fe371d
docs: improve use component guide (#11020) 2022-09-10 12:28:43 +08:00
neverland
5d8282ddae
docs(Toast): add component usage guide (#11019) 2022-09-10 11:40:11 +08:00
neverland
43ff890d79
feat(Toast): add message slot (#11018) 2022-09-10 11:32:38 +08:00
neverland
98642589ee
docs: add picker-group to migration guide (#11016) 2022-09-08 23:16:08 +08:00
neverland
0aac165d4e
fix(Picker): failed to update value in some cases (#11009) 2022-09-04 21:00:18 +08:00
chenjiahan
f60a3b840d docs(changelog): vant@4.0.0-rc.0 2022-09-04 14:51:32 +08:00
chenjiahan
997f3e0acd release: 4.0.0-rc.0 2022-09-04 14:49:15 +08:00
chenjiahan
e2fe50250c Merge branch 'dev' into next 2022-09-04 14:34:46 +08:00
chenjiahan
7d023fd6e4 docs: update babel-plugin-import tip 2022-09-04 09:59:42 +08:00
neverland
1afe960f30
feat: add new PickerGroup component (#11005)
* feat: add PickerGroup component

* chore: remove log

* chore: en doc

* chore: add snapshot

* docs: update
2022-09-04 09:39:37 +08:00
neverland
a677bee2b8
fix(DatePicker): min-date prop not work correctly (#10985) 2022-08-28 17:11:04 +08:00
neverland
05544c109f
fix(DatePicker): failed to update model value (#10984) 2022-08-28 16:59:41 +08:00
neverland
21c57caa9b
docs(DatePicker): fix toolbar slot name (#10983) 2022-08-28 11:52:14 +08:00
chenjiahan
66aa2906e0 docs(DatePicker): fix basic usage 2022-08-28 11:01:49 +08:00
chenjiahan
61ef6b9a98 chore(Picker): add getEventParams function 2022-08-28 10:50:49 +08:00
neverland
c25acb8d46
chore(PIcker): split PickerToolbar component (#10982) 2022-08-28 10:43:14 +08:00
chenjiahan
8bcb34724f fix(Picker): fix passive event 2022-08-28 10:01:14 +08:00
chenjiahan
cf5e7e6629 Merge branch 'dev' into next 2022-08-28 09:47:56 +08:00
neverland
50ee584953
docs(changelog): vant@4.0.0-beta.1 (#10966) 2022-08-24 22:53:35 +08:00
chenjiahan
889b28609a release: 4.0.0-beta.1 2022-08-24 22:47:17 +08:00
neverland
dcffa09812
feat(Popup): improve style when position is center (#10965) 2022-08-24 22:40:11 +08:00
chenjiahan
0881de82af docs(ImagePreview): fix cell style in demo 2022-08-24 22:19:13 +08:00
chenjiahan
86adc66dd9 Restore "fix(Popup&Toast): invalid adaptive width (#10961)"
This reverts commit aa1689fed8e58d52db2367263ccbcc08853ff848.
2022-08-24 22:04:46 +08:00
chenjiahan
1d87343195 Merge branch 'dev' into next 2022-08-24 22:04:34 +08:00
neverland
dfdc27a755
docs: add faq of remove babel-plugin-import (#10942)
* docs: add faq of remove babel-plugin-import

* docs: update
2022-08-20 22:35:12 +08:00
neverland
5c6992f97c
feat(ConfigProvider): add theme-vars-dark and theme-vars-light props (#10939) 2022-08-19 23:10:41 +08:00
chenjiahan
292ac6b55e Merge branch 'dev' into next 2022-08-19 22:14:47 +08:00
limingxin
8ef197ea12
docs: fix link address in migrate-from-v3.md (#10919) 2022-08-16 16:09:35 +08:00
neverland
f61b00175e
fix(ConfigProvider): should remove theme class on unmount (#10898) 2022-08-10 08:31:46 +08:00
chenjiahan
6a1516ca37 Merge branch 'dev' into next 2022-08-10 07:58:38 +08:00
neverland
d3c679aa27
docs: add babel-plugin-import migration guide (#10867) 2022-07-30 20:22:43 +08:00
neverland
2f6ef7aed8
feat(Picker): add clickOption event (#10865)
* feat(Picker): add clickOption event

* chore: update
2022-07-30 14:04:43 +08:00
chenjiahan
83a4e286bd Merge branch 'dev' into next 2022-07-23 22:20:49 +08:00
chenjiahan
178df39c44 Merge branch 'dev' into next 2022-07-23 21:20:27 +08:00
dyh333
627661e0ed
docs(Icon): separate usingUrl demo from basicUsage (#10828)
Co-authored-by: dingyihui <dingyihui@cmss.chinamobile.com>
2022-07-20 21:36:08 +08:00
neverland
b46b429682
docs(changelog): 4.0.0-beta.0 (#10825) 2022-07-16 15:07:38 +08:00
chenjiahan
15142770f2 release: 4.0.0-beta.0 2022-07-16 15:00:14 +08:00
chenjiahan
aea97f90e6 Merge branch 'dev' into next 2022-07-16 14:58:24 +08:00
chenjiahan
a0bd47dd84 Merge branch 'dev' into next 2022-07-10 21:23:47 +08:00
neverland
e998c1be95
feat: add @vant/compat package (#10806)
* feat: add @vant/compat package

* chore: remove prepare script
2022-07-09 20:54:27 +08:00
neverland
1ce400bb7f
refactor(Toast): redesign function-call API (#10804) 2022-07-09 17:09:54 +08:00
neverland
5a3fe7ed0f
chore: rename unreleased API (#10803) 2022-07-09 15:38:28 +08:00
neverland
1bc6cbdb69
refactor(ImagePreview): redesign function-call API (#10802) 2022-07-09 15:32:02 +08:00
neverland
5f526c9f0e
chore(Dialog): rename unreleased API (#10783) 2022-07-03 13:54:06 +08:00
neverland
ec78d5b1d9
refactor(Notify): redesign function-call API (#10782)
* refactor(Notify): redesign function-call API

* docs: update
2022-07-03 13:39:27 +08:00
neverland
e5e6e8aaa4
refactor(Dialog): redesign function-call API (#10781)
* refactor(Dialog): re-design function-call API

* chore: remove invalid char

* docs: order

* chore: remove var
2022-07-03 12:28:25 +08:00
chenjiahan
e27efdad0d Merge branch 'dev' into next 2022-07-03 10:57:43 +08:00
neverland
154408fa8b
perf: remove less source file to remove bundle size (#10752)
* perf: remove css source file to remove bundle size

* docs: update migration guide
2022-06-26 21:52:40 +08:00
chenjiahan
20ee462cab Merge branch 'dev' into next 2022-06-26 21:43:49 +08:00
chenjiahan
2b77f96fc5 Merge branch 'dev' into next 2022-06-25 12:48:53 +08:00
chenjiahan
1d5cb1e051 Merge branch 'dev' into next 2022-06-18 20:34:49 +08:00
chenjiahan
5d81dcb549 Merge branch 'dev' into next 2022-06-11 20:23:35 +08:00
chenjiahan
63519433f1 docs(changelog): 4.0.0-alpha.4 2022-05-31 20:12:05 +08:00
chenjiahan
1f19fb0d9f release: 4.0.0-alpha.4 2022-05-31 20:09:47 +08:00
chenjiahan
f13601cb9f Merge branch 'dev' into next 2022-05-31 20:05:10 +08:00
neverland
5cd731594f
feat: using mjs extension for esmodule (#10625) 2022-05-22 20:10:14 +08:00
chenjiahan
582582e480 Merge branch 'dev' into next 2022-05-22 19:59:35 +08:00
chenjiahan
fe2115ee5c docs(changelog): 4.0.0-alpha.3 2022-05-02 11:09:34 +08:00
chenjiahan
bc60f5116b release: 4.0.0-alpha.3 2022-05-02 11:07:49 +08:00
chenjiahan
b65396ef06 Merge branch 'dev' into next 2022-05-02 11:03:12 +08:00
Alan Wang
523d9ef400
fix(Search): style error in dark mode (#10527) 2022-04-23 09:58:09 +08:00
chenjiahan
912a5f17b1 docs(changelog): 4.0.0-alpha.2 2022-04-16 22:09:58 +08:00
chenjiahan
ea57c9e9ff release: 4.0.0-alpha.2 2022-04-16 22:08:38 +08:00
chenjiahan
ac7d6dd542 Merge branch 'dev' into next 2022-04-16 21:59:33 +08:00
chenjiahan
a0389defcc Merge branch 'dev' into next 2022-04-05 10:31:21 +08:00
chenjiahan
26cbffe6c1 chore: fix some Numeric type 2022-03-27 18:13:09 +08:00
chenjiahan
a541090f3a Merge branch 'dev' into next 2022-03-27 18:12:42 +08:00
chenjiahan
e08e9845a2 breaking change(AddressEdit): remove postal 2022-03-26 20:12:24 +08:00
chenjiahan
8e822a591d Merge branch 'dev' into next 2022-03-26 19:10:50 +08:00
chenjiahan
8e78b6d8ad Merge branch 'dev' into next 2022-03-23 09:11:13 +08:00
chenjiahan
5bff9e8c5f docs(changelog): 4.0.0-alpha.1 2022-03-19 20:08:46 +08:00
chenjiahan
475d108ccb release: 4.0.0-alpha.1 2022-03-19 20:06:05 +08:00
chenjiahan
527bad9cc2 Merge branch 'dev' into next 2022-03-19 20:04:31 +08:00
neverland
47a5151a20
docs(Picker): add v-model usage (#10417) 2022-03-19 20:02:56 +08:00
neverland
dd724fde4d
docs(DatePicker): fix formatter usage (#10416) 2022-03-19 19:51:03 +08:00
neverland
a584cc217b
fix(DatePicker): failed to update model value (#10415) 2022-03-19 19:48:28 +08:00
neverland
5b72c4005b
test: fix snapshots of cell (#10414) 2022-03-19 19:44:45 +08:00
neverland
d2f6a3c96f
fix(ConfigProvider): dark mode not work as default value (#10413) 2022-03-19 19:42:28 +08:00
TeRny
239be20cbf
fix(Calendar, Picker): Fix calendar and picker's title color in dark mode (#10403)
* 解决Dialog组件的标题、消息内容在深色模式下仍显示黑色的问题

* 解决Picker和Calendar组件的Title在深色模式下为黑色的问题

Co-authored-by: 朱坤 <zhukun.gz@chinatelecom.cn>
2022-03-17 20:34:45 +08:00
chenjiahan
101dcc3ef2 Merge branch 'dev' into next 2022-03-13 16:27:20 +08:00
TeRny
8572393881
fix(Dialog): Fix dialog title and message color in dark mode (#10379)
Co-authored-by: 朱坤 <zhukun.gz@chinatelecom.cn>
2022-03-11 14:56:39 +08:00
neverland
0d79eb244b
docs: update 4.0.0-alpha changelog (#10339) 2022-02-27 20:57:46 +08:00
chenjiahan
db2e6ca12f docs: fix publicPath 2022-02-21 13:39:21 +08:00
chenjiahan
992eea3eeb build: fix workflow name 2022-02-21 12:27:25 +08:00
chenjiahan
3a1e792e31 docs: update 2022-02-21 12:26:31 +08:00
chenjiahan
06199faf2c build: deploy v4 site 2022-02-21 12:22:46 +08:00
chenjiahan
c206642d2d release: 4.0.0-alpha.0 2022-02-21 12:13:17 +08:00
chenjiahan
5bade79394 chore: add npm tag 2022-02-21 12:11:18 +08:00
chenjiahan
2ceb68e691 docs: update Vant 4 2022-02-20 21:02:09 +08:00
chenjiahan
9cfef027aa docs(Form): fix Picker demo 2022-02-20 20:54:16 +08:00
chenjiahan
0b4b04eefb chore: fix Cell usage 2022-02-20 20:48:20 +08:00
chenjiahan
0a7b535533 style(Cell): remove value aline style 2022-02-20 20:47:01 +08:00
chenjiahan
17c2022a6f chore(AddressEdit): avoid using innerHTML 2022-02-20 20:36:04 +08:00
chenjiahan
cce3ceb5b6 style(TreeSelect): using primary color 2022-02-20 20:24:11 +08:00
chenjiahan
fcd91cb0b0 style(AddressList): using primary color 2022-02-20 20:22:49 +08:00
chenjiahan
3692014070 style(Card): blue tag 2022-02-20 20:19:27 +08:00
chenjiahan
85c5ad6718 style(CouponList): change default color 2022-02-20 20:17:05 +08:00
chenjiahan
bef919c7f0 style: increase font-weight bold 2022-02-20 10:04:39 +08:00
chenjiahan
d39f6a898a style(ContactList): using primary color 2022-02-18 14:22:25 +08:00
chenjiahan
43ae30026a style(ContactEdit): adjust submit button 2022-02-18 14:11:54 +08:00
chenjiahan
27c46b01ae types(AddressEdit): SearchItem optional 2022-02-18 11:36:55 +08:00
chenjiahan
3979ddc40a fix(AddressEdit): selectedOption maybe undefined 2022-02-17 16:35:07 +08:00
chenjiahan
967cb56c18 types(Picker): fix return type of getSelectedOptions 2022-02-17 16:25:53 +08:00
chenjiahan
c65d4f9672 style(AddressEdit): submit button using primary type 2022-02-17 16:16:55 +08:00
chenjiahan
73788f9e88 style(Switch): adjust width 2022-02-17 15:36:23 +08:00
chenjiahan
2acd47b5ee style(Switch): adjust style 2022-02-17 15:31:52 +08:00
chenjiahan
b3cf1dfb54 breaking change(AddressEdit): adjust change-area event params 2022-02-16 12:14:01 +08:00
chenjiahan
35a50012c7 feat(Area): add getSelectedOptions method 2022-02-16 12:02:46 +08:00
chenjiahan
83295e655d feat(Picker): add getSelectedOptions method 2022-02-16 12:00:32 +08:00
chenjiahan
e2790fe95d chore(DatePicker): reuse genOptions 2022-02-16 11:06:58 +08:00
chenjiahan
b608fb23f0 types(TimePicker): export TimePickerColumnType 2022-02-16 10:59:31 +08:00
chenjiahan
829db06d5b types(DatePicker): export DatePickerColumnType 2022-02-16 10:59:20 +08:00
chenjiahan
2e053ad76b feat(TimePicker): support select second 2022-02-16 10:57:09 +08:00
chenjiahan
c942efb76b docs(DatePicker): fix currentDate of year-month 2022-02-16 10:56:46 +08:00
chenjiahan
fdcf9931be refactor(TimePicker): modelValue now is string array 2022-02-15 17:44:53 +08:00
chenjiahan
e61bd487fa fix(Picker): should update modelValue immediately 2022-02-15 17:36:49 +08:00
chenjiahan
7deba88b91 docs(Form): fix demo 2022-02-15 16:08:31 +08:00
chenjiahan
980f976011 fix(DatePicker): allow modelValue to be empty 2022-02-15 15:55:09 +08:00
chenjiahan
c00fa4cd70 chore: remove legacy DatetimePicker 2022-02-15 15:54:24 +08:00
chenjiahan
ef8e66a924 refactor(DatePicker): v-model now is string array 2022-02-15 15:42:23 +08:00
chenjiahan
e8ffc80dc6 feat(DatePicker): date columns calculation 2022-02-15 15:08:59 +08:00
chenjiahan
3920ebe25b Merge branch 'dev' into next 2022-02-14 11:03:20 +08:00
chenjiahan
5b407566db refactor: DatePicker Component 2022-02-14 10:55:10 +08:00
chenjiahan
f327d5bbc2 test(TimePicker): migrate test cases 2022-02-10 19:48:18 +08:00
chenjiahan
2a8bb86fbb fix(Picker): should update selectedValues correctly 2022-02-10 19:41:19 +08:00
chenjiahan
7dae50b0c9 fix(TimePicker): should update modelValue immediately 2022-02-10 17:56:51 +08:00
chenjiahan
d68053ea7f docs: update migration guide 2022-02-10 16:50:20 +08:00
chenjiahan
8ca9741845 breaking change: event name become camelCase 2022-02-10 16:49:45 +08:00
chenjiahan
c9c312c4ba test: update all snapshots 2022-02-10 16:22:18 +08:00
chenjiahan
5dca6fe2e2 chore: remove unused snapshot 2022-02-10 16:20:34 +08:00
chenjiahan
fdc5436249 test(TimePicker): demo snapshot 2022-02-10 16:19:31 +08:00
chenjiahan
7d22cc2155 fix(TimePicker): remove columns-order prop 2022-02-10 15:57:57 +08:00
chenjiahan
42d1dd836c refactor: TimePicker component 2022-02-10 15:12:21 +08:00
chenjiahan
15e7461a93 docs: docsearch version v4 2022-02-09 16:35:58 +08:00
chenjiahan
0fdaf133bb Merge branch 'dev' into next 2022-02-09 16:31:47 +08:00
chenjiahan
d4584aafda docs: dark mode 2022-02-09 16:31:07 +08:00
chenjiahan
5b3c4f1546 style: update van doc background 2022-02-09 11:34:25 +08:00
chenjiahan
25302fb333 Merge branch 'dev' into next 2022-02-09 11:20:28 +08:00
chenjiahan
2f02f19cec Merge branch 'dev' into next 2022-02-08 14:56:38 +08:00
chenjiahan
82e0e454d0 style(CouponList): update field background color in dark mode 2022-01-28 10:15:46 +08:00
chenjiahan
650bdd2294 feat(ActionBar): update icon text color 2022-01-28 10:15:08 +08:00
chenjiahan
cb2d99c8cf style(Card): improve card mode style 2022-01-28 10:14:45 +08:00
chenjiahan
42f03fe1a2 style(GridItem): adust text color 2022-01-28 10:04:01 +08:00
chenjiahan
fc2847fdb4 style(Slider): improve dark mode color 2022-01-27 19:44:00 +08:00
chenjiahan
d6e80e8954 style(IndexBar): increase font weight of active index 2022-01-27 11:21:00 +08:00
chenjiahan
39b51d5dae style(Tabbar): adjust text color 2022-01-27 11:19:32 +08:00
chenjiahan
8e98e97449 style(Field): adjust label color 2022-01-27 11:07:02 +08:00
chenjiahan
f5c32c29f1 style(@vant/cli): improve mobile style in dark mode 2022-01-27 11:00:38 +08:00
chenjiahan
98b999d911 refactor: rename --van-background-light to --van-background-2 2022-01-27 10:35:57 +08:00
chenjiahan
0e8e3200a2 style(@vant/cli): improve demo style in dark mode 2022-01-26 17:05:47 +08:00
chenjiahan
73322fd310 feat: add dark mode basic variables 2022-01-26 16:00:16 +08:00
chenjiahan
97b074ad0c feat(ConfigProvider): dark theme class 2022-01-26 15:59:52 +08:00
chenjiahan
541458c41b refactor: attach css variables to body element 2022-01-26 15:58:48 +08:00
chenjiahan
df24a3b683 feat(@vant/cli): mobile site support dark mode 2022-01-26 15:57:28 +08:00
chenjiahan
966af9dd68 Merge branch 'dev' into next 2022-01-26 14:12:40 +08:00
chenjiahan
b6fcd32dcf style(@vant/cli): adjust css vars 2022-01-26 10:26:15 +08:00
chenjiahan
35a990ed65 feat(@vant/cli): desktop site support dark mode 2022-01-25 20:03:05 +08:00
chenjiahan
f9573402a7 feat(ConfigProvider): add theme prop 2022-01-25 15:38:57 +08:00
chenjiahan
437dcc6c41 Merge branch 'dev' into next 2022-01-25 14:42:56 +08:00
chenjiahan
5a15660c77 style(Steps): update default button border color 2022-01-24 16:58:56 +08:00
chenjiahan
0706d464b6 test(Area): update all test cases 2022-01-24 16:43:57 +08:00
chenjiahan
8d49b88748 fix(Area): allow to reset modelValue 2022-01-24 16:43:40 +08:00
chenjiahan
5900affe37 test(Picker): update cascade test cases 2022-01-24 16:09:08 +08:00
chenjiahan
bda3617d14 style(Button): adjust font-smoothing 2022-01-24 15:54:32 +08:00
chenjiahan
bd24288a12 style(Button): update default button border color 2022-01-24 15:37:49 +08:00
chenjiahan
3b10d66bb9 style(Tabs): using primary color by default 2022-01-24 15:19:46 +08:00
chenjiahan
41b1cd7185 style(Stepper): using primary color by default 2022-01-24 15:19:25 +08:00
chenjiahan
eeb233bf99 style(Sidebar): using primary color by default 2022-01-24 15:18:28 +08:00
chenjiahan
8b95db1574 style(IndexBar): using primary color by default 2022-01-24 15:17:50 +08:00
chenjiahan
37e7c618b8 style(DropdownMenu): using primary color by default 2022-01-24 15:17:33 +08:00
chenjiahan
84b90b1f4c style(Cascader): using primary color by default 2022-01-24 15:16:58 +08:00
chenjiahan
4e89e88845 style(Calendar): using primary color by default 2022-01-24 15:15:02 +08:00
chenjiahan
d867710cc9 style(Button): increase default border radius from 2px to 4px 2022-01-23 21:39:04 +08:00
chenjiahan
526f54595d chore: fix van-background-light var in demos 2022-01-21 17:19:21 +08:00
chenjiahan
3c3f58a7be Merge branch 'next' of github.com:youzan/vant into next 2022-01-21 16:52:51 +08:00
chenjiahan
e4ab2156f1 test(Picker): update basic test cases 2022-01-21 16:52:32 +08:00
chenjiahan
6bba2906bc chore(Area): adjust some codes 2022-01-21 16:52:32 +08:00
chenjiahan
95cf27745b chore: remove empty area.ts 2022-01-21 16:52:32 +08:00
chenjiahan
a9baa3b2d1 docs(Area): update documents 2022-01-21 16:52:32 +08:00
chenjiahan
0186219586 fix(Picker): error when options is empty 2022-01-21 16:52:32 +08:00
chenjiahan
cca428aac6 refactor: Area component 2022-01-21 16:52:32 +08:00
chenjiahan
113e1b7a62 fix(Picker): currentColumns calculation 2022-01-21 16:52:32 +08:00
chenjiahan
ff69fdacc7 fix(Picker): failed to update modelValue 2022-01-21 16:52:32 +08:00
chenjiahan
6c64bc33c1 types(Picker): fix columns prop typing 2022-01-21 16:52:32 +08:00
chenjiahan
fda3f1be94 docs(@vant/cli): changelog 4.0.0-rc.6 2022-01-21 16:52:32 +08:00
chenjiahan
e93ae7b430 release: @vant/cli 4.0.0-rc.6 2022-01-21 16:52:32 +08:00
zoy-l
4ee1a3d766 fix(vant-cli): pnpm compatibility (#10214)
* fix(vant-cli): pnpm compatibility

* chore: clean code, semantic naming
2022-01-21 16:52:32 +08:00
chenjiahan
cc10eb1f2a docs(Picker): new document 2022-01-21 16:52:32 +08:00
chenjiahan
d16075f39e fix(Picker): option.className not work 2022-01-21 16:52:32 +08:00
chenjiahan
f436b5ae09 docs: update migration guide 2022-01-21 16:52:32 +08:00
chenjiahan
ceaab349f1 docs(Picker): update demo 2022-01-21 16:52:32 +08:00
chenjiahan
22654bf518 fix(Picker): fix some bugs 2022-01-21 16:52:32 +08:00
chenjiahan
c061412138 refactor(Picker): rename item-height to option-height 2022-01-21 16:52:32 +08:00
chenjiahan
89b029aa42 refactor(Picker): data driven 2022-01-21 16:52:32 +08:00
chenjiahan
2c7bafe3c1 Merge branch 'dev' into next 2022-01-19 15:54:57 +08:00
chenjiahan
bcb7eabff3 Merge branch 'dev' into next 2022-01-17 19:48:33 +08:00
chenjiahan
e63f471c77 Merge branch 'dev' into next 2021-12-11 10:41:38 +08:00
chenjiahan
1fc72c68dd Merge branch 'dev' into next 2021-11-19 11:00:11 +08:00
chenjiahan
1bd2ab00ac Merge branch 'dev' into next 2021-11-15 10:59:04 +08:00
chenjiahan
e713e8eade Merge branch 'dev' into next 2021-11-10 15:31:33 +08:00
chenjiahan
26fa234026 Merge branch 'dev' into next 2021-11-04 20:21:28 +08:00
chenjiahan
6c24fa728e docs: update v4 migration guide 2021-11-04 17:48:37 +08:00
chenjiahan
0fedd1dbea Merge branch 'dev' into next 2021-11-04 11:26:29 +08:00
chenjiahan
722f73dfbe Merge branch 'dev' into next 2021-11-03 17:47:44 +08:00
chenjiahan
7d6916b946 breaking change: shorter css var names 2021-11-03 17:36:17 +08:00
chenjiahan
ada5db011c breaking change: no longer support less vars 2021-11-03 16:45:49 +08:00
chenjiahan
099141f6c5 breaking change(Tabs): remove click、disabled event 2021-11-03 16:05:37 +08:00
chenjiahan
6a4033b6bc breaking change(Picker): remove default slot、value-key prop 2021-11-03 16:03:15 +08:00
703 changed files with 17618 additions and 17943 deletions

View File

@ -1,4 +1,4 @@
name: Deploy V3 Site
name: Deploy V4 Site
on:
push:
@ -15,7 +15,7 @@ jobs:
- name: Checkout 🛎️
uses: actions/checkout@v2
with:
ref: 'dev'
ref: 'next'
- name: Install pnpm
run: npm i pnpm@7 -g
@ -36,4 +36,5 @@ jobs:
with:
branch: gh-pages
folder: packages/vant/site-dist
target-folder: v4
clean: false

View File

@ -2,7 +2,7 @@ name: Sync to Gitee
on:
push:
branches: [dev, 2.x, gh-pages]
branches: [dev, 2.x, 3.x, gh-pages]
workflow_dispatch:

View File

@ -1,7 +1,4 @@
/**
* 同步父窗口和 iframe vue-router 状态
*/
import { ref } from 'vue';
import { config } from 'site-desktop-shared';
let queue = [];
@ -62,6 +59,49 @@ export function syncPathToChild() {
}
}
export function syncThemeToChild(theme) {
const iframe = document.querySelector('iframe');
if (iframe) {
iframeReady(() => {
iframe.contentWindow.postMessage(
{
type: 'updateTheme',
value: theme,
},
'*'
);
});
}
}
export function getDefaultTheme() {
const cache = window.localStorage.getItem('vantTheme');
if (cache) {
return cache;
}
const useDark =
window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches;
return useDark ? 'dark' : 'light';
}
export function useCurrentTheme() {
const theme = ref(getDefaultTheme());
window.addEventListener('message', (event) => {
if (event.data?.type !== 'updateTheme') {
return;
}
const newTheme = event.data?.value || '';
theme.value = newTheme;
});
return theme;
}
export function listenToSyncPath(router) {
window.addEventListener('message', (event) => {
if (event.data?.type !== 'replacePath') {

View File

@ -1,15 +1,15 @@
@import './var';
@import './vars.less';
body {
min-width: 1100px;
margin: 0;
overflow-x: auto;
color: @van-doc-black;
color: var(--van-doc-text-color-2);
font-size: 16px;
font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Helvetica Neue',
Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui',
'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
background-color: @van-doc-background-color;
background-color: var(--van-doc-background);
-webkit-font-smoothing: antialiased;
}
@ -41,8 +41,8 @@ a {
.van-doc-row {
width: 100%;
@media (min-width: @van-doc-row-max-width) {
width: @van-doc-row-max-width;
@media (min-width: var(--van-doc-row-max-width)) {
width: var(--van-doc-row-max-width);
margin: 0 auto;
}
}

View File

@ -1,20 +1,18 @@
@import './var';
code {
position: relative;
display: block;
padding: 16px 20px;
overflow-x: auto;
color: @van-doc-code-color;
color: var(--van-doc-code-color);
font-weight: 400;
font-size: 14px;
font-family: @van-doc-code-font-family;
font-family: 'Source Code Pro', 'Monaco', 'Inconsolata', monospace;
line-height: 26px;
white-space: pre-wrap;
word-wrap: break-word;
-webkit-font-smoothing: auto;
background-color: #f8f8f8;
border-radius: @van-doc-border-radius;
background-color: var(--van-doc-code-background);
border-radius: var(--van-doc-border-radius);
}
pre {
@ -33,7 +31,7 @@ pre {
}
.hljs-subst {
color: @van-doc-code-color;
color: var(--van-doc-code-color);
}
.hljs-string,
@ -42,18 +40,18 @@ pre {
.hljs-template-tag,
.hljs-template-variable,
.hljs-addition {
color: @van-doc-green;
color: var(--van-doc-green);
}
.hljs-comment,
.hljs-quote {
color: #999;
color: var(--van-doc-code-comment-color);
}
.hljs-params,
.hljs-keyword,
.hljs-attribute {
color: @van-doc-purple;
color: var(--van-doc-purple);
}
.hljs-deletion,

View File

@ -1,27 +0,0 @@
@van-doc-black: #323233;
@van-doc-blue: #1989fa;
@van-doc-purple: #8080ff;
@van-doc-fuchsia: #a7419e;
@van-doc-green: #4fc08d;
@van-doc-text-color: #34495e;
@van-doc-text-light-blue: rgba(69, 90, 100, 0.6);
@van-doc-background-color: #f7f8fa;
@van-doc-grey: #999;
@van-doc-dark-grey: #666;
@van-doc-light-grey: #ccc;
@van-doc-border-color: #f1f4f8;
@van-doc-code-color: #58727e;
@van-doc-code-background-color: #f1f4f8;
@van-doc-code-font-family: 'Source Code Pro', 'Monaco', 'Inconsolata', monospace;
@van-doc-padding: 24px;
@van-doc-row-max-width: 1680px;
@van-doc-nav-width: 220px;
@van-doc-border-radius: 20px;
// header
@van-doc-header-top-height: 64px;
@van-doc-header-bottom-height: 50px;
// simulator
@van-doc-simulator-width: 360px;
@van-doc-simulator-height: 620px;

View File

@ -0,0 +1,74 @@
body {
// colors
--van-doc-black: #000;
--van-doc-white: #fff;
--van-doc-gray-1: #f7f8fa;
--van-doc-gray-2: #f2f3f5;
--van-doc-gray-3: #ebedf0;
--van-doc-gray-4: #dcdee0;
--van-doc-gray-5: #c8c9cc;
--van-doc-gray-6: #969799;
--van-doc-gray-7: #646566;
--van-doc-gray-8: #323233;
--van-doc-blue: #1989fa;
--van-doc-green: #07c160;
// sizes
--van-doc-padding: 24px;
--van-doc-row-max-width: 1680px;
--van-doc-nav-width: 220px;
--van-doc-border-radius: 20px;
--van-doc-simulator-width: 360px;
--van-doc-simulator-height: 620px;
--van-doc-header-top-height: 64px;
}
.van-doc-theme-light {
// text
--van-doc-text-color-1: var(--van-doc-black);
--van-doc-text-color-2: var(--van-doc-gray-8);
--van-doc-text-color-3: #34495e;
--van-doc-text-color-4: var(--van-doc-gray-6);
--van-doc-link-color: var(--van-doc-blue);
// background
--van-doc-background: #eff2f5;
--van-doc-background-2: var(--van-doc-white);
--van-doc-background-3: var(--van-doc-white);
--van-doc-header-background: #011f3c;
--van-doc-border-color: var(--van-doc-gray-2);
// code
--van-doc-code-color: #58727e;
--van-doc-code-comment-color: var(--van-doc-gray-6);
--van-doc-code-background: var(--van-doc-gray-1);
// blockquote
--van-doc-blockquote-color: #4994df;
--van-doc-blockquote-background: #ecf9ff;
}
.van-doc-theme-dark {
// text
--van-doc-text-color-1: var(--van-doc-white);
--van-doc-text-color-2: rgba(255, 255, 255, 0.9);
--van-doc-text-color-3: rgba(255, 255, 255, 0.75);
--van-doc-text-color-4: rgba(255, 255, 255, 0.6);
--van-doc-link-color: #1bb5fe;
// background
--van-doc-background: #202124;
--van-doc-background-2: rgba(255, 255, 255, 0.06);
--van-doc-background-3: rgba(255, 255, 255, 0.1);
--van-doc-header-background: rgba(1, 31, 60, 0.3);
--van-doc-border-color: #3a3a3c;
// code
--van-doc-code-color: rgba(200, 200, 200, 0.85);
--van-doc-code-comment-color: var(--van-doc-gray-7);
--van-doc-code-background: rgba(0, 0, 0, 0.24);
// blockquote
--van-doc-blockquote-color: #bae6fd;
--van-doc-blockquote-background: rgba(7, 89, 133, 0.25);
}

View File

@ -8,6 +8,7 @@
:simulator="simulator"
:has-simulator="hasSimulator"
:lang-configs="langConfigs"
:dark-mode-class="darkModeClass"
>
<router-view />
</van-doc>
@ -27,6 +28,7 @@ export default {
data() {
return {
hasSimulator: true,
darkModeClass: config.site.darkModeClass,
};
},
@ -70,18 +72,18 @@ export default {
watch: {
// eslint-disable-next-line
'$route.path'() {
this.setTitleAndToogleSimulator();
this.setTitleAndToggleSimulator();
},
lang(val) {
setLang(val);
this.setTitleAndToogleSimulator();
this.setTitleAndToggleSimulator();
},
config: {
handler(val) {
if (val) {
this.setTitleAndToogleSimulator();
this.setTitleAndToggleSimulator();
}
},
immediate: true,
@ -100,7 +102,7 @@ export default {
},
methods: {
setTitleAndToogleSimulator() {
setTitleAndToggleSimulator() {
let { title } = this.config;
const navItems = this.config.nav.reduce(

View File

@ -18,18 +18,18 @@ export default {
</script>
<style lang="less">
@import '../../common/style/var';
.van-doc-container {
box-sizing: border-box;
padding-left: @van-doc-nav-width;
padding-left: var(--van-doc-nav-width);
overflow: hidden;
&--with-simulator {
padding-right: @van-doc-simulator-width + @van-doc-padding;
padding-right: calc(
var(--van-doc-simulator-width) + var(--van-doc-padding)
);
@media (max-width: 1100px) {
padding-right: @van-doc-simulator-width - 8px;
padding-right: calc(var(--van-doc-simulator-width) - 8px);
}
}
}

View File

@ -82,14 +82,11 @@ export default {
</script>
<style lang="less">
@import '../../common/style/var';
.van-doc-card {
margin-bottom: 24px;
padding: 24px;
background-color: #fff;
border-radius: @van-doc-border-radius;
box-shadow: 0 8px 12px #ebedf0;
background-color: var(--van-doc-background-2);
border-radius: var(--van-doc-border-radius);
overflow: auto;
> pre code {
@ -145,15 +142,15 @@ export default {
> table a,
> blockquote a {
margin: 0 1px;
color: @van-doc-blue;
color: var(--van-doc-link-color);
-webkit-font-smoothing: auto;
&:hover {
color: darken(@van-doc-blue, 10%);
opacity: 0.8;
}
&:active {
color: darken(@van-doc-blue, 20%);
opacity: 0.6;
}
}
@ -161,7 +158,6 @@ export default {
> h4,
> h5,
> h6 {
color: @van-doc-black;
font-weight: normal;
line-height: 1.6;
@ -190,23 +186,19 @@ export default {
> p {
margin-top: 8px;
}
> p,
> blockquote p {
color: @van-doc-text-color;
color: var(--van-doc-text-color-3);
font-size: 15px;
line-height: 26px;
strong {
color: black;
color: var(--van-doc-text-color-1);
}
}
> table {
width: 100%;
margin-top: 12px;
color: @van-doc-text-color;
color: var(--van-doc-text-color-3);
font-size: 14px;
line-height: 1.5;
border-collapse: collapse;
@ -227,7 +219,7 @@ export default {
td {
padding: 8px;
border-top: 1px solid @van-doc-code-background-color;
border-top: 1px solid var(--van-doc-border-color);
&:first-child {
padding-left: 0;
@ -236,10 +228,10 @@ export default {
code {
margin: 0;
padding: 2px 6px;
color: @van-doc-blue;
color: var(--van-doc-blue);
font-weight: 600;
font-size: 11px;
background-color: fade(@van-doc-blue, 10%);
background-color: rgba(25, 137, 250, 0.15);
border-radius: 20px;
}
}
@ -251,9 +243,9 @@ export default {
em {
display: inline-block;
color: @van-doc-green;
color: var(--van-doc-green);
font-size: 14px;
font-family: @van-doc-code-font-family;
font-family: 'Source Code Pro', 'Monaco', 'Inconsolata', monospace;
font-style: normal;
max-width: 300px;
-webkit-font-smoothing: auto;
@ -269,7 +261,7 @@ export default {
position: relative;
margin: 5px 0 5px 10px;
padding-left: 15px;
color: @van-doc-text-color;
color: var(--van-doc-text-color-3);
font-size: 15px;
line-height: 26px;
@ -281,7 +273,7 @@ export default {
width: 6px;
height: 6px;
margin-top: 10px;
border: 1px solid @van-doc-dark-grey;
border: 1px solid currentColor;
border-radius: 50%;
content: '';
}
@ -311,15 +303,17 @@ export default {
> blockquote {
margin: 16px 0 0;
padding: 16px;
background-color: #ecf9ff;
border-radius: @van-doc-border-radius;
font-size: 14px;
color: var(--van-doc-blockquote-color);
background-color: var(--van-doc-blockquote-background);
border-radius: var(--van-doc-border-radius);
}
> img,
> p img {
width: 100%;
margin: 16px 0;
border-radius: @van-doc-border-radius;
border-radius: var(--van-doc-border-radius);
}
}
@ -334,7 +328,6 @@ export default {
h1,
h2 {
color: @van-doc-black;
font-weight: normal;
line-height: 1.5;

View File

@ -24,6 +24,16 @@
</a>
</li>
<li v-if="darkModeClass" class="van-doc-header__top-nav-item">
<a
class="van-doc-header__link"
target="_blank"
@click="toggleTheme"
>
<img :src="themeImg" />
</a>
</li>
<li
ref="version"
v-if="versions"
@ -69,6 +79,7 @@
<script>
import SearchInput from './SearchInput.vue';
import { packageVersion } from 'site-desktop-shared';
import { getDefaultTheme, syncThemeToChild } from '../../common/iframe-sync';
export default {
name: 'VanDocHeader',
@ -82,10 +93,12 @@ export default {
config: Object,
versions: Array,
langConfigs: Array,
darkModeClass: String,
},
data() {
return {
currentTheme: getDefaultTheme(),
packageVersion,
showVersionPop: false,
};
@ -112,9 +125,32 @@ export default {
searchConfig() {
return this.config.searchConfig;
},
themeImg() {
if (this.currentTheme === 'light') {
return 'https://b.yzcdn.cn/vant/dark-theme.svg';
}
return 'https://b.yzcdn.cn/vant/light-theme.svg';
},
},
watch: {
currentTheme: {
handler(newVal, oldVal) {
window.localStorage.setItem('vantTheme', newVal);
document.body.classList.remove(`van-doc-theme-${oldVal}`);
document.body.classList.add(`van-doc-theme-${newVal}`);
syncThemeToChild(newVal);
},
immediate: true,
},
},
methods: {
toggleTheme() {
this.currentTheme = this.currentTheme === 'light' ? 'dark' : 'light';
},
toggleVersionPop() {
const val = !this.showVersionPop;
@ -147,18 +183,16 @@ export default {
</script>
<style lang="less">
@import '../../common/style/var';
.van-doc-header {
width: 100%;
background-color: #001938;
background-color: var(--van-doc-header-background);
user-select: none;
&__top {
display: flex;
align-items: center;
height: @van-doc-header-top-height;
padding: 0 @van-doc-padding;
height: var(--van-doc-header-top-height);
padding: 0 var(--van-doc-padding);
&-nav {
flex: 1;
@ -234,7 +268,7 @@ export default {
transition: 0.2s;
&:hover {
color: @van-doc-blue;
color: var(--van-doc-link-color);
background-color: #f7f8fa;
}
}
@ -268,6 +302,8 @@ export default {
}
&__link {
cursor: pointer;
span {
color: #fff;
font-size: 16px;

View File

@ -71,22 +71,19 @@ export default {
</script>
<style lang="less">
@import '../../common/style/var';
.van-doc-nav {
position: fixed;
left: 0;
z-index: 1;
min-width: @van-doc-nav-width;
max-width: @van-doc-nav-width;
padding: @van-doc-padding 0;
min-width: var(--van-doc-nav-width);
max-width: var(--van-doc-nav-width);
padding: 8px 0;
overflow-y: scroll;
background-color: #fff;
box-shadow: 0 8px 12px #ebedf0;
background-color: var(--van-doc-background-2);
@media (min-width: @van-doc-row-max-width) {
@media (min-width: var(--van-doc-row-max-width)) {
left: 50%;
margin-left: -(@van-doc-row-max-width / 2);
margin-left: calc((var(--van-doc-row-max-width) / 2 * -1));
}
&::-webkit-scrollbar {
@ -110,8 +107,8 @@ export default {
}
&__title {
padding: 8px 0 8px @van-doc-padding;
color: #455a64;
padding: 24px 0 0 var(--van-doc-padding);
color: var(--van-doc-text-color-2);
font-weight: 600;
font-size: 15px;
line-height: 28px;
@ -121,21 +118,19 @@ export default {
a {
display: block;
margin: 8px 0;
padding: 8px 0 8px @van-doc-padding;
color: #455a64;
padding: 6px 0 6px var(--van-doc-padding);
color: var(--van-doc-text-color-3);
font-size: 14px;
line-height: 20px;
transition: color 0.2s;
&:hover,
&.active {
color: @van-doc-green;
color: var(--van-doc-link-color);
}
&.active {
font-weight: 600;
background-color: #ebfff0;
border-radius: 999px;
}
span {

View File

@ -38,8 +38,6 @@ export default {
</script>
<style lang="less">
@import '../../common/style/var';
#docsearch {
display: inline-block;
vertical-align: middle;

View File

@ -44,34 +44,31 @@ export default {
</script>
<style lang="less">
@import '../../common/style/var';
.van-doc-simulator {
position: absolute;
top: @van-doc-padding + @van-doc-header-top-height;
right: @van-doc-padding;
top: calc(var(--van-doc-padding) + var(--van-doc-header-top-height));
right: var(--van-doc-padding);
z-index: 1;
box-sizing: border-box;
width: @van-doc-simulator-width;
min-width: @van-doc-simulator-width;
width: var(--van-doc-simulator-width);
min-width: var(--van-doc-simulator-width);
overflow: hidden;
background: #fafafa;
border-radius: @van-doc-border-radius;
box-shadow: 0 8px 12px #ebedf0;
background: var(--van-doc-background-2);
border-radius: var(--van-doc-border-radius);
@media (max-width: 1100px) {
right: auto;
left: 750px;
}
@media (min-width: @van-doc-row-max-width) {
@media (min-width: var(--van-doc-row-max-width)) {
right: 50%;
margin-right: -(@van-doc-row-max-width / 2) + 24px;
margin-right: calc(var(--van-doc-row-max-width) / 2 * -1 + 24px);
}
&-fixed {
position: fixed;
top: @van-doc-padding;
top: var(--van-doc-padding);
}
iframe {

View File

@ -5,6 +5,7 @@
:config="config"
:versions="versions"
:lang-configs="langConfigs"
:dark-mode-class="darkModeClass"
@switch-version="$emit('switch-version', $event)"
/>
<doc-nav :lang="lang" :nav-config="config.nav" />
@ -39,8 +40,9 @@ export default {
lang: String,
versions: Array,
simulator: String,
hasSimulator: Boolean,
langConfigs: Array,
hasSimulator: Boolean,
darkModeClass: String,
config: {
type: Object,
required: true,
@ -108,7 +110,3 @@ export default {
},
};
</script>
<style lang="less">
@import '../../common/style/var';
</style>

View File

@ -3,7 +3,7 @@ import { createRouter, createWebHashHistory } from 'vue-router';
import { isMobile, decamelize } from '../common';
import { config, documents } from 'site-desktop-shared';
import { getLang, setDefaultLang } from '../common/locales';
import { listenToSyncPath, syncPathToChild } from '../common/iframe-router';
import { listenToSyncPath, syncPathToChild } from '../common/iframe-sync';
if (isMobile) {
location.replace('mobile.html' + location.hash);

View File

@ -10,21 +10,49 @@
</template>
<script>
import { watch } from 'vue';
import DemoNav from './components/DemoNav.vue';
import { useCurrentTheme } from '../common/iframe-sync';
import { config } from 'site-mobile-shared';
export default {
components: { DemoNav },
setup() {
const theme = useCurrentTheme();
watch(
theme,
(newVal, oldVal) => {
document.body.classList.remove(`van-doc-theme-${oldVal}`);
document.body.classList.add(`van-doc-theme-${newVal}`);
const { darkModeClass } = config.site;
if (darkModeClass) {
document.body.classList.toggle(darkModeClass, newVal === 'dark');
}
},
{ immediate: true }
);
},
};
</script>
<style lang="less">
@import '../common/style/var';
@import '../common/style/base';
body {
min-width: 100vw;
}
.van-doc-theme-light {
background-color: var(--van-doc-gray-1);
}
.van-doc-theme-dark {
background-color: var(--van-doc-black);
}
::-webkit-scrollbar {
width: 0;
background: transparent;

View File

@ -20,13 +20,11 @@ export default {
</script>
<style lang="less">
@import '../../common/style/var';
.van-doc-demo-block {
&__title {
margin: 0;
padding: 32px 16px 16px;
color: @van-doc-text-light-blue;
color: var(--van-doc-text-color-4);
font-weight: normal;
font-size: 14px;
line-height: 16px;

View File

@ -52,14 +52,11 @@ export default {
</script>
<style lang="less">
@import '../../common/style/var';
.demo-home {
box-sizing: border-box;
width: 100%;
min-height: 100vh;
padding: 46px 20px 20px;
background: #fff;
&__title,
&__desc {
@ -94,7 +91,7 @@ export default {
&__desc {
margin: 0 0 40px;
color: rgba(69, 90, 100, 0.6);
color: var(--van-doc-text-color-4);
font-size: 14px;
}
}

View File

@ -43,12 +43,10 @@ export default {
</script>
<style lang="less">
@import '../../common/style/var';
.demo-home-nav {
&__title {
margin: 24px 0 8px 16px;
color: rgba(69, 90, 100, 0.6);
color: var(--van-doc-text-color-4);
font-size: 14px;
}
@ -57,20 +55,20 @@ export default {
display: flex;
margin: 0 0 12px;
padding-left: 20px;
color: #323233;
color: var(--van-doc-text-color-3);
font-weight: 600;
font-size: 14px;
line-height: 40px;
background: #f7f8fa;
background-color: var(--van-doc-background-3);
border-radius: 99px;
transition: background 0.3s;
transition: opacity 0.3s;
&:hover {
background: darken(#f7f8fa, 3%);
opacity: 0.8;
}
&:active {
background: darken(#f7f8fa, 6%);
opacity: 0.6;
}
}

View File

@ -42,7 +42,7 @@ export default {
align-items: center;
justify-content: center;
height: 56px;
background-color: #fff;
background-color: var(--van-doc-background-3);
&__title {
font-weight: 600;

View File

@ -4,7 +4,7 @@ import DemoHome from './components/DemoHome.vue';
import { decamelize } from '../common';
import { demos, config } from 'site-mobile-shared';
import { getLang, setDefaultLang } from '../common/locales';
import { listenToSyncPath, syncPathToParent } from '../common/iframe-router';
import { listenToSyncPath, syncPathToParent } from '../common/iframe-sync';
const { locales, defaultLang } = config.site;

View File

@ -0,0 +1,10 @@
MIT License
Copyright (c) Youzan
Copyright (c) Chen Jiahan and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,32 @@
# @vant/compat
This package provides Vant 3 compatible behavior for Vant 4 users.
## Install
```shell
# with npm
npm i @vant/compat
# with yarn
yarn add @vant/compat
# with pnpm
pnpm add @vant/compat
```
## Usage
```js
// Same as Toast in Vant 3
import { Toast } from '@vant/compat';
// Same as Dialog in Vant 3
import { Dialog } from '@vant/compat';
// Same as Notify in Vant 3
import { Notify } from '@vant/compat';
// Same as ImagePreview in Vant 3
import { ImagePreview } from '@vant/compat';
```

View File

@ -0,0 +1 @@
require('../vant-use/build');

View File

@ -0,0 +1,52 @@
{
"name": "@vant/compat",
"version": "1.0.0",
"description": "Provide Vant 3 compatible behavior for Vant 4",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.esm.mjs",
"require": "./dist/index.cjs.js"
}
},
"sideEffects": false,
"files": [
"dist"
],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"clean": "rimraf ./dist",
"dev": "node ./build.js -w",
"build:types": "tsc -p ./tsconfig.json --emitDeclarationOnly",
"build:bundle": "node ./build.js",
"build": "pnpm clean && pnpm build:bundle && pnpm build:types",
"release": "pnpm build && release-it"
},
"repository": {
"type": "git",
"url": "https://github.com/vant-ui/vant.git",
"directory": "packages/vant-compat"
},
"bugs": "https://github.com/vant-ui/vant/issues",
"author": "chenjiahan",
"license": "MIT",
"devDependencies": {
"@vue/runtime-core": "^3.2.27",
"vant": "workspace:*",
"vue": "^3.2.27",
"esbuild": "^0.14.29",
"release-it": "^15.1.1",
"typescript": "^4.7.4"
},
"release-it": {
"git": {
"tag": false,
"commitMessage": "release: @vant/compat ${version}"
}
}
}

View File

@ -0,0 +1,30 @@
import {
Dialog as VanDialog,
showDialog,
closeDialog,
showConfirmDialog,
setDialogDefaultOptions,
resetDialogDefaultOptions,
} from 'vant';
import type { App } from 'vue';
export const Dialog = (...args: Parameters<typeof showDialog>) =>
showDialog(...args);
Dialog.Component = VanDialog;
Dialog.alert = Dialog;
Dialog.config = showConfirmDialog;
Dialog.close = closeDialog;
Dialog.setDefaultOptions = setDialogDefaultOptions;
Dialog.resetDefaultOptions = resetDialogDefaultOptions;
Dialog.install = (app: App) => {
app.use(Dialog.Component);
app.config.globalProperties.$dialog = Dialog;
};
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$dialog: typeof Dialog;
}
}

View File

@ -0,0 +1,11 @@
import { ImagePreview as VanImagePreview, showImagePreview } from 'vant';
import type { App } from 'vue';
export const ImagePreview = (...args: Parameters<typeof showImagePreview>) =>
showImagePreview(...args);
ImagePreview.Component = VanImagePreview;
ImagePreview.install = (app: App) => {
app.use(ImagePreview.Component);
};

View File

@ -0,0 +1,4 @@
export * from './toast';
export * from './notify';
export * from './dialog';
export * from './image-preview';

View File

@ -0,0 +1,27 @@
import {
Notify as VanNotify,
showNotify,
closeNotify,
setNotifyDefaultOptions,
resetNotifyDefaultOptions,
} from 'vant';
import type { App } from 'vue';
export const Notify = (...args: Parameters<typeof showNotify>) =>
showNotify(...args);
Notify.clear = closeNotify;
Notify.Component = VanNotify;
Notify.setDefaultOptions = setNotifyDefaultOptions;
Notify.resetDefaultOptions = resetNotifyDefaultOptions;
Notify.install = (app: App) => {
app.use(Notify.Component);
app.config.globalProperties.$notify = Notify;
};
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$notify: typeof Notify;
}
}

View File

@ -0,0 +1,49 @@
import {
showToast,
closeToast,
showFailToast,
showSuccessToast,
allowMultipleToast,
setToastDefaultOptions,
resetToastDefaultOptions,
} from 'vant';
import type { App } from 'vue';
export const Toast = (...args: Parameters<typeof showToast>) => {
const toast = showToast(...args);
return {
clear: toast.close,
...toast,
};
};
Toast.fail = (...args: Parameters<typeof showFailToast>) => {
const toast = showFailToast(...args);
return {
clear: toast.close,
...toast,
};
};
Toast.success = (...args: Parameters<typeof showSuccessToast>) => {
const toast = showSuccessToast(...args);
return {
clear: toast.close,
...toast,
};
};
Toast.clear = closeToast;
Toast.allowMultiple = allowMultipleToast;
Toast.setDefaultOptions = setToastDefaultOptions;
Toast.resetDefaultOptions = resetToastDefaultOptions;
Toast.install = (app: App) => {
app.config.globalProperties.$toast = Toast;
};
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$toast: typeof Toast;
}
}

View File

@ -0,0 +1,8 @@
{
"extends": "../../tsconfig",
"compilerOptions": {
"outDir": "./dist",
"declaration": true
},
"include": ["src/**/*"]
}

View File

@ -31,7 +31,7 @@
"repository": {
"type": "git",
"url": "https://github.com/vant-ui/vant.git",
"directory": "packages/vant-markdown-loader"
"directory": "packages/vant-popperjs"
},
"bugs": "https://github.com/vant-ui/vant/issues",
"author": "chenjiahan",

View File

@ -14,7 +14,7 @@ function bundleBundle(format) {
outfile,
// preserve Chinese character
charset: 'utf8',
external: ['vue'],
external: ['vue', 'vant'],
entryPoints: ['./src/index.ts'],
}).then(finish);
}

View File

@ -1,7 +1,7 @@
import { ref, Ref } from 'vue';
import { inBrowser } from '../utils';
type VisibilityState = "hidden" | "visible";
type VisibilityState = 'hidden' | 'visible';
let visibility: Ref<VisibilityState>;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -38,9 +38,9 @@ pnpm dev
仓库的不同分支对应不同的 Vant 版本,请切换到对应分支进行开发:
- dev 分支对应 Vant 4 版本,适用于 Vue 3
- 3.x 分支对应 Vant 3 版本 ,适用于 Vue 3
- 2.x 分支对应 Vant 2 版本,适用于 Vue 2
- dev 分支对应 Vant 3 版本 ,适用于 Vue 3
- next 分支对应 Vant 4 版本,适用于 Vue 3
### 镜像仓库

View File

@ -31,7 +31,7 @@ Vant 是一个**轻量、可靠的移动端组件库**,于 2017 年开源。
### 版本提示
你当前浏览的是 **Vant 3.x 版本** 的文档,适用于 Vue 3 开发。如果你在使用 Vue 2请浏览 [Vant 2 文档](https://vant-contrib.gitee.io/vant/v2)。
你当前浏览的是 **Vant 4.x 版本** 的文档,适用于 Vue 3 开发。如果你在使用 Vue 2请浏览 [Vant 2 文档](https://vant-contrib.gitee.io/vant/v2)。
### 快速上手

View File

@ -1,4 +1,4 @@
# 从 v2 升级
# 从 v2 升级到 v3
### 介绍

View File

@ -0,0 +1,365 @@
# 从 v3 升级到 v4
### 介绍
本文档提供了从 Vant 3 到 Vant 4 的升级指南。
## 按需引入方式调整
### 移除 babel-plugin-import
从 Vant 4.0 版本开始,将不再支持 `babel-plugin-import`,请移除项目中依赖的 `babel-plugin-import` 插件。
只需要删除 `babel.config.js` 中的以下代码即可:
```diff
module.exports = {
plugins: [
- ['import', {
- libraryName: 'vant',
- libraryDirectory: 'es',
- style: true
- }, 'vant']
]
};
```
#### 收益
移除 `babel-plugin-import` 有以下收益:
- 不再强依赖 babel项目可以使用 esbuild、swc 等更高效的编译工具,大幅度提升编译效率。
- 不再受到 `babel-plugin-import` 的 import 写法限制,可以从 vant 中导入除了组件以外的其他内容,比如 Vant 4 中新增的 `showToast` 等方法:
```ts
import { showToast, showDialog } from 'vant';
```
#### 样式引入方案
移除 `babel-plugin-import` 对项目的 JS 体积不会有影响,因为 Vant 默认支持通过 Tree Shaking 优化来移除不需要的 JS 代码。
而 CSS 代码的引入方式可以从以下两种方式中进行选择:
- 通过 [unplugin-vue-components](https://github.com/antfu/unplugin-vue-components) 插件实现按需引入样式,详细用法参见 [快速上手](#/zh-CN/quickstart)。
- 在项目中全量引入 Vant 的样式文件:
```js
import 'vant/lib/index.css';
```
## 组件重构
### 介绍
在 Vant 4 中,一共有三个组件被完全重构,它们是:
- `Area`
- `Picker`
- `DatetimePicker`
之所以重构这三个组件,是因为在之前的版本中,`Picker` 组件的 API 设计存在一些不合理的设计,导致大家在使用时经常遇到问题,比如:
- columns 数据格式定义不合理,容易产生误解
- 数据流不清晰,暴露了过多的实例方法来对数据进行操作
为了解决上述问题,我们在 v4 版本中对 `Picker` 组件进行了重构,同时也重构了基于 `Picker` 派生出的 `Area``DatetimePicker` 组件。
### Picker 组件重构
#### 主要变更
- 支持通过 `v-model` 绑定当前选中的值,移除 `default-index` 属性
- 重新定义了 `columns` 属性的结构
- 移除了操作内部数据的实例方法,仅保留 `confirm` 方法
- 新增 `getSelectedOptions` 实例方法
- 调整了 `confirm``cancel``change` 事件的参数
- 重命名 `item-height` 属性为 `option-height`
- 重命名 `visible-item-count` 属性为 `visible-option-num`
> 详细用法请参见 [Picker 组件文档](#/zh-CN/picker)。
### DatetimePicker 组件重构
DatetimePicker 组件被拆分为:
- [TimePicker](#/zh-CN/time-picker): 用于时间选择。
- [DatePicker](#/zh-CN/date-picker): 用于日期选择。
- [PickerGroup](#/zh-CN/picker-group): 用于用于结合多个 Picker 选择器组件。
同时TimePicker 和 DatePicker 组件也基于新版 Picker 组件进行重构,并优化了部分 API 设计。
#### 主要变更
以下是 TimePicker 和 DatePicker 的主要 API 变化,更多细节请参考 [TimePicker](#/zh-CN/time-picker) 和 [DatePicker](#/zh-CN/date-picker) 文档。
- `v-model` 绑定的值调整为数组格式
- 新增 `columns-type` 属性,用于控制选项类型和顺序
- 移除 `type` 属性和 `columns-order` 属性
- 移除 `getPicker` 方法
- 调整 `confirm``cancel``change` 事件的参数,与 Picker 组件保持一致
> Vant 4 不再提供旧版的 DatetimePicker 组件,使用 PickerGroup 组件可以实现更灵活、更丰富的交互效果,具体用法请参考 [PickerGroup](#/zh-CN/picker-group) 组件文档。
### Area 组件重构
Area 组件是基于 Picker 组件进行封装的,因此本次升级也对 Area 组件进行了内部逻辑的重构,并优化了部分 API 设计。
#### 主要变更
- 支持通过 `v-model` 绑定当前选中的值
- 移除 `reset` 方法,现在可以通过修改 `v-model` 来进行重置
- 移除 `is-oversea-code` 属性
- 调整 `confirm``cancel``change` 事件的参数,与 Picker 组件保持一致
- 重命名 `value` 属性为 `modelValue`
- 重命名 `item-height` 属性为 `option-height`
- 重命名 `visible-item-count` 属性为 `visible-option-num`
> 详细用法请参见 [Area 组件文档](#/zh-CN/area)。
## API 调整
### Dialog 调用方式调整
在 Vant 3 中,`Dialog` 是一个函数,调用函数可以快速唤起全局的弹窗组件,而 `Dialog.Component` 才是 `Dialog` 组件对象,这与大部分组件的用法存在差异,容易导致使用错误。
为了更符合直觉,我们在 Vant 4 中调整了 `Dialog` 的调用方式,将 `Dialog()` 函数重命名为 `showDialog()`
```js
// Vant 3
Dialog(); // 函数调用
Dialog.Component; // 组件对象
// Vant 4
showDialog(); // 函数调用
Dialog; // 组件对象
```
`Dialog` 上挂载的其他方法也进行了重命名,新旧 API 的映射关系如下:
```js
Dialog(); // -> showDialog()
Dialog.alert(); // -> showDialog()
Dialog.confirm(); // -> showConfirmDialog()
Dialog.close(); // -> closeDialog();
Dialog.setDefaultOptions(); // -> setDialogDefaultOptions()
Dialog.resetDefaultOptions(); // -> resetDialogDefaultOptions()
```
#### 兼容方案
为了便于代码迁移,我们提供了兼容方案,你可以使用 `@vant/compat` 中导出的 `Dialog` 对象来兼容原有代码。
```js
import { Dialog } from '@vant/compat';
Dialog();
Dialog.close();
```
`@vant/compat` 中导出的 `Dialog` 与 Vant 3 中的 `Dialog` 拥有完全一致的 API 和行为,因此你只需要修改 `Dialog` 的引用路径,其他代码可以保持不变。
### Toast 调用方式调整
Vant 4 中,`Toast` 组件的调用方式也进行了调整,与 `Dialog` 组件的改动一致:
```js
// Vant 3
Toast(); // 函数调用
// Vant 4
showToast(); // 函数调用
Toast; // 组件对象
```
`Toast` 上挂载的其他方法也进行了重命名,新旧 API 的映射关系如下:
```js
Toast(); // -> showToast()
Toast.fail(); // -> showFailToast()
Toast.success(); // -> showSuccessToast()
Toast.loading(); // -> showLoadingToast()
Toast.clear(); // -> closeToast()
Toast.setDefaultOptions(); // -> setToastDefaultOptions()
Toast.resetDefaultOptions(); // -> resetToastDefaultOptions()
```
同时Vant 4 将不再在 `this` 对象上全局注册 `$toast` 方法,这意味着 `this` 对象上将无法访问到 `$toast`
#### 兼容方案
为了便于代码迁移,我们提供了兼容方案,你可以使用 `@vant/compat` 中导出的 `Toast` 对象来兼容原有代码。
```js
import { Toast } from '@vant/compat';
Toast();
Toast.clear();
```
`@vant/compat` 中导出的 `Toast` 与 Vant 3 中的 `Toast` 拥有完全一致的 API 和行为,因此你只需要修改 `Toast` 的引用路径,其他代码可以保持不变。
### Notify 调用方式调整
Vant 4 中,`Notify` 组件的调用方式也进行了调整,与 `Dialog` 组件的改动一致:
```js
// Vant 3
Notify(); // 函数调用
Notify.Component; // 组件对象
// Vant 4
showNotify(); // 函数调用
Notify; // 组件对象
```
`Notify` 上挂载的其他方法也进行了重命名,新旧 API 的映射关系如下:
```js
Notify(); // -> showNotify()
Notify.clear(); // -> closeNotify()
Notify.setDefaultOptions(); // -> setNotifyDefaultOptions()
Notify.resetDefaultOptions(); // -> resetNotifyDefaultOptions()
```
同时Vant 4 将不再在 `this` 对象上全局注册 `$notify` 方法,这意味着 `this` 对象上将无法访问到 `$notify`
#### 兼容方案
为了便于代码迁移,我们提供了兼容方案,你可以使用 `@vant/compat` 中导出的 `Notify` 对象来兼容原有代码。
```js
import { Notify } from '@vant/compat';
Notify();
Notify.clear();
```
`@vant/compat` 中导出的 `Notify` 与 Vant 3 中的 `Notify` 拥有完全一致的 API 和行为,因此你只需要修改 `Notify` 的引用路径,其他代码可以保持不变。
### ImagePreview 调用方式调整
Vant 4 中,`ImagePreview` 组件的调用方式也进行了调整,与 `ImagePreview` 组件的改动一致:
```js
// Vant 3
ImagePreview(); // 函数调用
ImagePreview.Component; // 组件对象
// Vant 4
showImagePreview(); // 函数调用
ImagePreview; // 组件对象
```
#### 兼容方案
为了便于代码迁移,我们提供了兼容方案,你可以使用 `@vant/compat` 中导出的 `ImagePreview` 对象来兼容原有代码。
```js
import { ImagePreview } from '@vant/compat';
ImagePreview();
```
`@vant/compat` 中导出的 `ImagePreview` 与 Vant 3 中的 `ImagePreview` 拥有完全一致的 API 和行为,因此你只需要修改 `ImagePreview` 的引用路径,其他代码可以保持不变。
### 事件命名调整
从 Vant 4 开始,所有的事件均采用 Vue 官方推荐的**驼峰格式**进行命名。
```js
// Vant 3
emit('click-input');
// Vant 4
emit('clickInput');
```
这项改动**不影响原有的模板代码**Vue 会自动在模板中对事件名进行格式转换:
```html
<!-- 以下代码可以照常运行,无须做任何更改 -->
<van-field @click-input="onClick" />
```
如果你在 JSX 中使用 Vant 组件,需要将监听的事件名调整为驼峰格式,新的监听方式更加符合 JSX 本身的规范:
```jsx
// Vant 3
<Field onClick-input={onClick} />
// Vant 4
<Field onClickInput={onClick} />
```
### 其他 API 调整
在 Vant 4.0 版本中,以下 API 进行了不兼容更新:
#### AddressEdit
- 移除 `show-postal` 属性
- 移除 `postal-validator` 属性
- `change-area` 事件的参数调整为 `PickerOption[]` 类型
- 移除未在文档中标注的 `getArea` 实例方法
#### Popup
Popup 的 CSS 样式进行了一定调整,请确认是否对项目中的 UI 产生影响。
- 默认添加了 `box-sizing: border-box` 样式
- 调整了 `position="center"` 时的水平居中方式,以解决弹窗宽度无法正确自适应的问题:
```less
// Vant 3
.van-popup--center {
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
// Vant 4
.van-popup--center {
left: 0;
right: 0;
width: fit-content;
max-width: calc(100vw - var(--van-padding-md) * 2);
margin: 0 auto;
transform: translateY(-50%);
}
```
#### Tabs
- 移除了 `click``disabled` 事件,请使用 `click-tab` 事件代替
## 样式变量调整
### 移除 Less 变量
目前 Vant 已经支持了基于 CSS 变量的主题定制能力,因此后续将不再提供基于 Less 的主题定制方式。
这意味着 Vant 的 npm 包中将不再会包含 `.less` 样式源文件,只会提供编译后的 `.css` 样式文件。
如果你的项目正在使用旧版的 Less 主题定制,请使用 [ConfigProvider 全局配置](#/zh-CN/config-provider) 组件进行替换。
### 简化 CSS 变量名
考虑到 **代码体积****使用便捷性**,我们对部分 CSS 变量的名称进行了简化,在变量名中使用更简短的单词,涉及以下变更:
```less
animation-duration -> duration
animation-timing-function-enter -> ease-out
animation-timing-function-leave -> ease-in
background-color -> background
background-color-light -> background-2
border-radius -> radius
border-width-base -> border-width
box-shadow -> shadow
font-family -> font
font-weight-bold -> font-bold
price-integer-font -> price-font
text-link -> link
transition-duration -> duration
```
由于涉及的 CSS 变量较多,建议在代码仓库中进行全局匹配和替换。

View File

@ -50,7 +50,7 @@ The easiest way to use Vant is to include a CDN link in the HTML file, after whi
app.use(vant.Lazyload);
// Call function component
vant.Toast('Message');
vant.showToast('Message');
app.mount('#app');
</script>
@ -195,19 +195,19 @@ Some components of Vant are provided as function, including `Toast`, `Dialog`, `
```js
// Toast
import { Toast } from 'vant';
import { showToast } from 'vant';
import 'vant/es/toast/style';
// Dialog
import { Dialog } from 'vant';
import { showDialog } from 'vant';
import 'vant/es/dialog/style';
// Notify
import { Notify } from 'vant';
import { showNotify } from 'vant';
import 'vant/es/notify/style';
// ImagePreview
import { ImagePreview } from 'vant';
import { showImagePreview } from 'vant';
import 'vant/es/image-preview/style';
```

View File

@ -54,8 +54,8 @@ pnpm add vant
// 可以通过下面的方式手动注册
app.use(vant.Lazyload);
// 调用函数组件,弹出一个 Toast
vant.Toast('提示');
// 调用工具函数,弹出一个 Toast
vant.showToast('提示');
app.mount('#app');
</script>
@ -200,19 +200,19 @@ Vant 中有个别组件是以函数的形式提供的,包括 `Toast``Dialog
```js
// Toast
import { Toast } from 'vant';
import { showToast } from 'vant';
import 'vant/es/toast/style';
// Dialog
import { Dialog } from 'vant';
import { showDialog } from 'vant';
import 'vant/es/dialog/style';
// Notify
import { Notify } from 'vant';
import { showNotify } from 'vant';
import 'vant/es/notify/style';
// ImagePreview
import { ImagePreview } from 'vant';
import { showImagePreview } from 'vant';
import 'vant/es/image-preview/style';
```

View File

@ -1,176 +0,0 @@
# Custom Theme
### Deprecated
This document is deprecated. Vant provides a more convenient [ConfigProvider](#/en-US/config-provider) component for theme configuration. Less variables **will be removed in the next major version**.
### Intro
Vant use [Less](http://lesscss.org/) as css preprocessoryou can override the default less variables to custom theme.
### Less variables
There are some [basic variables](<(https://github.com/vant-ui/vant/blob/dev/packages/vant/src/style/var.less)>) below, for component less variables, please refer to the documentation of each component, or view the `var.less` file in the component source directory.
```less
// Color Palette
@black: #000;
@white: #fff;
@gray-1: #f7f8fa;
@gray-2: #f2f3f5;
@gray-3: #ebedf0;
@gray-4: #dcdee0;
@gray-5: #c8c9cc;
@gray-6: #969799;
@gray-7: #646566;
@gray-8: #323233;
@red: #ee0a24;
@blue: #1989fa;
@orange: #ff976a;
@orange-dark: #ed6a0c;
@orange-light: #fffbe8;
@green: #07c160;
// Gradient Colors
@gradient-red: linear-gradient(to right, #ff6034, #ee0a24);
@gradient-orange: linear-gradient(to right, #ffd01e, #ff8917);
// Component Colors
@text-color: @gray-8;
@active-color: @gray-2;
@active-opacity: 0.7;
@disabled-opacity: 0.5;
@background-color: @gray-1;
@background-color-light: @white;
@text-link-color: #576b95;
// Padding
@padding-base: 4px;
@padding-xs: @padding-base * 2;
@padding-sm: @padding-base * 3;
@padding-md: @padding-base * 4;
@padding-lg: @padding-base * 6;
@padding-xl: @padding-base * 8;
// Font
@font-size-xs: 10px;
@font-size-sm: 12px;
@font-size-md: 14px;
@font-size-lg: 16px;
@font-weight-bold: 500;
@line-height-xs: 14px;
@line-height-sm: 18px;
@line-height-md: 20px;
@line-height-lg: 22px;
@base-font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue',
Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB',
'Microsoft Yahei', sans-serif;
@price-integer-font-family: Avenir-Heavy, PingFang SC, Helvetica Neue, Arial,
sans-serif;
// Animation
@animation-duration-base: 0.3s;
@animation-duration-fast: 0.2s;
@animation-timing-function-enter: ease-out;
@animation-timing-function-leave: ease-in;
// Border
@border-color: @gray-3;
@border-width-base: 1px;
@border-radius-sm: 2px;
@border-radius-md: 4px;
@border-radius-lg: 8px;
@border-radius-max: 999px;
```
## How to custom theme
### Step 1: import less file
First you should import the less source file to your project. you can use babel-plugin-import to automatically import or just manually import less file.
#### Automatically import style
Configure babel plugin in babel.config.js, if you are using babel6, please manually import less file.
```js
module.exports = {
plugins: [
[
'import',
{
libraryName: 'vant',
libraryDirectory: 'es',
// specify less file path
style: (name) => `${name}/style/less`,
},
'vant',
],
],
};
```
#### Manually import style
```js
// import all styles
import 'vant/lib/index.less';
// import style of single component
import 'vant/lib/button/style/less';
```
### Step 2: modify less variables
Use [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) provided by less.js to modify less variableswebpack config for reference:
```js
// webpack.config.js
module.exports = {
rules: [
{
test: /\.less$/,
use: [
// ...other loaders
{
loader: 'less-loader',
options: {
lessOptions: {
modifyVars: {
// override with less vars
'text-color': '#111',
'border-color': '#eee',
// or override with less file
hack: `true; @import "your-less-file-path.less";`,
},
},
},
},
],
},
],
};
```
If you build a project by vue-cli,it can be configured in `vue.config.js`:
```js
// vue.config.js
module.exports = {
css: {
loaderOptions: {
less: {
lessOptions: {
modifyVars: {
// override with less vars
'text-color': '#111',
'border-color': '#eee',
// or override with less file
hack: `true; @import "your-less-file-path.less";`,
},
},
},
},
},
};
```

View File

@ -1,184 +0,0 @@
# 定制主题
### 废弃提示
本文档已废弃Vant 提供了更方便的 [ConfigProvider 全局配置](#/zh-CN/config-provider) 组件进行主题配置。基于 Less 变量进行定制的方式**将在下个大版本废弃**。
### 介绍
Vant 提供了一套默认主题CSS 命名采用 BEM 的风格,方便使用者覆盖样式。如果你想完全替换主题色或者其他样式,可以按照本文档进行主题定制。
### 示例工程
我们提供了一个基于 Vue CLI 3 的示例工程,仓库地址为 [Vant Demo](https://github.com/vant-ui/vant-demo),其中包含了定制主题的基本配置,可以作为参考。
### 样式变量
Vant 使用了 [Less](http://lesscss.org/) 对样式进行预处理,并内置了一些样式变量,通过替换样式变量即可定制你自己需要的主题。
下面是所有的[基础样式变量](https://github.com/vant-ui/vant/blob/dev/packages/vant/src/style/var.less),组件的样式变量请参考各个组件的文档,或查看组件源码目录下的 `var.less` 文件。
```less
// Color Palette
@black: #000;
@white: #fff;
@gray-1: #f7f8fa;
@gray-2: #f2f3f5;
@gray-3: #ebedf0;
@gray-4: #dcdee0;
@gray-5: #c8c9cc;
@gray-6: #969799;
@gray-7: #646566;
@gray-8: #323233;
@red: #ee0a24;
@blue: #1989fa;
@orange: #ff976a;
@orange-dark: #ed6a0c;
@orange-light: #fffbe8;
@green: #07c160;
// Gradient Colors
@gradient-red: linear-gradient(to right, #ff6034, #ee0a24);
@gradient-orange: linear-gradient(to right, #ffd01e, #ff8917);
// Component Colors
@text-color: @gray-8;
@active-color: @gray-2;
@active-opacity: 0.7;
@disabled-opacity: 0.5;
@background-color: @gray-1;
@background-color-light: @white;
@text-link-color: #576b95;
// Padding
@padding-base: 4px;
@padding-xs: @padding-base * 2;
@padding-sm: @padding-base * 3;
@padding-md: @padding-base * 4;
@padding-lg: @padding-base * 6;
@padding-xl: @padding-base * 8;
// Font
@font-size-xs: 10px;
@font-size-sm: 12px;
@font-size-md: 14px;
@font-size-lg: 16px;
@font-weight-bold: 500;
@line-height-xs: 14px;
@line-height-sm: 18px;
@line-height-md: 20px;
@line-height-lg: 22px;
@base-font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue',
Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB',
'Microsoft Yahei', sans-serif;
@price-integer-font-family: Avenir-Heavy, PingFang SC, Helvetica Neue, Arial,
sans-serif;
// Animation
@animation-duration-base: 0.3s;
@animation-duration-fast: 0.2s;
@animation-timing-function-enter: ease-out;
@animation-timing-function-leave: ease-in;
// Border
@border-color: @gray-3;
@border-width-base: 1px;
@border-radius-sm: 2px;
@border-radius-md: 4px;
@border-radius-lg: 8px;
@border-radius-max: 999px;
```
## 定制方法
### 步骤一 引入样式源文件
定制主题时,需要引入组件对应的 Less 样式文件,支持按需引入和手动引入两种方式。
#### 按需引入样式(推荐)
在 babel.config.js 中配置按需引入样式源文件,注意 babel 6 不支持按需引入样式,请手动引入样式。
```js
module.exports = {
plugins: [
[
'import',
{
libraryName: 'vant',
libraryDirectory: 'es',
// 指定样式路径
style: (name) => `${name}/style/less`,
},
'vant',
],
],
};
```
#### 手动引入样式
```js
// 引入全部样式
import 'vant/lib/index.less';
// 引入单个组件样式
import 'vant/lib/button/style/less';
```
### 步骤二 修改样式变量
使用 Less 提供的 [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) 即可对变量进行修改,下面是参考的 webpack 配置。
```js
// webpack.config.js
module.exports = {
rules: [
{
test: /\.less$/,
use: [
// ...其他 loader 配置
{
loader: 'less-loader',
options: {
// 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
lessOptions: {
modifyVars: {
// 直接覆盖变量
'text-color': '#111',
'border-color': '#eee',
// 或者可以通过 less 文件覆盖(文件路径为绝对路径)
hack: `true; @import "your-less-file-path.less";`,
},
},
},
},
],
},
],
};
```
如果 vue-cli 搭建的项目,可以在 `vue.config.js` 中进行配置。
```js
// vue.config.js
module.exports = {
css: {
loaderOptions: {
less: {
// 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
lessOptions: {
modifyVars: {
// 直接覆盖变量
'text-color': '#111',
'border-color': '#eee',
// 或者可以通过 less 文件覆盖(文件路径为绝对路径)
hack: `true; @import "your-less-file-path.less";`,
},
},
},
},
},
};
```

View File

@ -52,6 +52,7 @@ export function initDemoLocale() {
disabled: '禁用状态',
uneditable: '不可编辑',
basicUsage: '基础用法',
usingUrl: '使用图片 URL',
advancedUsage: '高级用法',
loadingStatus: '加载状态',
},
@ -78,6 +79,7 @@ export function initDemoLocale() {
disabled: 'Disabled',
uneditable: 'Uneditable',
basicUsage: 'Basic Usage',
usingUrl: 'Using URL',
advancedUsage: 'Advanced Usage',
loadingStatus: 'Loading',
},

View File

@ -1,13 +1,16 @@
{
"name": "vant",
"version": "3.6.2",
"description": "Lightweight Mobile UI Components built on Vue",
"version": "4.0.0-rc.0",
"description": "Mobile UI Components built on Vue",
"main": "lib/vant.cjs.js",
"module": "es/index.mjs",
"style": "lib/index.css",
"typings": "lib/index.d.ts",
"unpkg": "lib/vant.min.js",
"jsdelivr": "lib/vant.min.js",
"npm": {
"tag": "next"
},
"files": [
"es",
"lib"
@ -66,8 +69,7 @@
"sideEffects": [
"es/**/style/*",
"lib/**/style/*",
"*.css",
"*.less"
"*.css"
],
"web-types": "lib/web-types.json"
}

View File

@ -17,7 +17,7 @@ import { Button, ButtonType } from '../button';
const [name, bem] = createNamespace('action-bar-button');
const actionBarButtonProps = extend({}, routeProps, {
export const actionBarButtonProps = extend({}, routeProps, {
type: String as PropType<ButtonType>,
text: String,
icon: String,

View File

@ -1,29 +1,27 @@
@import './var.less';
:root {
--van-action-bar-button-height: @action-bar-button-height;
--van-action-bar-button-warning-color: @action-bar-button-warning-color;
--van-action-bar-button-danger-color: @action-bar-button-danger-color;
body {
--van-action-bar-button-height: 40px;
--van-action-bar-button-warning-color: var(--van-gradient-orange);
--van-action-bar-button-danger-color: var(--van-gradient-red);
}
.van-action-bar-button {
flex: 1;
height: var(--van-action-bar-button-height);
font-weight: var(--van-font-weight-bold);
font-weight: var(--van-font-bold);
font-size: var(--van-font-size-md);
border: none;
border-radius: 0;
&--first {
margin-left: 5px;
border-top-left-radius: var(--van-border-radius-max);
border-bottom-left-radius: var(--van-border-radius-max);
border-top-left-radius: var(--van-radius-max);
border-bottom-left-radius: var(--van-radius-max);
}
&--last {
margin-right: 5px;
border-top-right-radius: var(--van-border-radius-max);
border-bottom-right-radius: var(--van-border-radius-max);
border-top-right-radius: var(--van-radius-max);
border-bottom-right-radius: var(--van-radius-max);
}
&--warning {

View File

@ -3,6 +3,7 @@ import _ActionBarButton from './ActionBarButton';
export const ActionBarButton = withInstall(_ActionBarButton);
export default ActionBarButton;
export { actionBarButtonProps } from './ActionBarButton';
export type { ActionBarButtonProps } from './ActionBarButton';
declare module 'vue' {

View File

@ -1,5 +0,0 @@
@import '../style/var.less';
@action-bar-button-height: 40px;
@action-bar-button-warning-color: var(--van-gradient-orange);
@action-bar-button-danger-color: var(--van-gradient-red);

View File

@ -12,7 +12,7 @@ import { Badge, type BadgeProps } from '../badge';
const [name, bem] = createNamespace('action-bar-icon');
const actionBarIconProps = extend({}, routeProps, {
export const actionBarIconProps = extend({}, routeProps, {
dot: Boolean,
text: String,
icon: String,

View File

@ -1,14 +1,12 @@
@import './var.less';
:root {
--van-action-bar-icon-width: @action-bar-icon-width;
--van-action-bar-icon-height: @action-bar-icon-height;
--van-action-bar-icon-color: @action-bar-icon-color;
--van-action-bar-icon-size: @action-bar-icon-size;
--van-action-bar-icon-font-size: @action-bar-icon-font-size;
--van-action-bar-icon-active-color: @action-bar-icon-active-color;
--van-action-bar-icon-text-color: @action-bar-icon-text-color;
--van-action-bar-icon-background-color: @action-bar-icon-background-color;
body {
--van-action-bar-icon-width: 48px;
--van-action-bar-icon-height: 100%;
--van-action-bar-icon-color: var(--van-text-color);
--van-action-bar-icon-size: 18px;
--van-action-bar-icon-font-size: var(--van-font-size-xs);
--van-action-bar-icon-active-color: var(--van-active-color);
--van-action-bar-icon-text-color: var(--van-text-color);
--van-action-bar-icon-background: var(--van-background-2);
}
.van-action-bar-icon {
@ -21,7 +19,7 @@
font-size: var(--van-action-bar-icon-font-size);
line-height: 1;
text-align: center;
background: var(--van-action-bar-icon-background-color);
background: var(--van-action-bar-icon-background);
cursor: pointer;
&:active {

View File

@ -3,6 +3,7 @@ import _ActionBarIcon from './ActionBarIcon';
export const ActionBarIcon = withInstall(_ActionBarIcon);
export default ActionBarIcon;
export { actionBarIconProps } from './ActionBarIcon';
export type { ActionBarIconProps } from './ActionBarIcon';
declare module 'vue' {

View File

@ -1,10 +0,0 @@
@import '../style/var.less';
@action-bar-icon-width: 48px;
@action-bar-icon-height: 100%;
@action-bar-icon-color: var(--van-text-color);
@action-bar-icon-size: 18px;
@action-bar-icon-font-size: var(--van-font-size-xs);
@action-bar-icon-active-color: var(--van-active-color);
@action-bar-icon-text-color: var(--van-gray-7);
@action-bar-icon-background-color: var(--van-background-color-light);

View File

@ -7,7 +7,7 @@ const [name, bem] = createNamespace('action-bar');
export const ACTION_BAR_KEY = Symbol(name);
const actionBarProps = {
export const actionBarProps = {
placeholder: Boolean,
safeAreaInsetBottom: truthProp,
};

View File

@ -32,12 +32,12 @@ app.use(ActionBarButton);
```
```js
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
const onClickIcon = () => Toast('Click Icon');
const onClickButton = () => Toast('Click Button');
const onClickIcon = () => showToast('Click Icon');
const onClickButton = () => showToast('Click Button');
return {
onClickIcon,
onClickButton,
@ -155,7 +155,7 @@ The component provides the following CSS variables, which can be used to customi
| Name | Default Value | Description |
| --- | --- | --- |
| --van-action-bar-background-color | _var(--van-background-color-light)_ | - |
| --van-action-bar-background | _var(--van-background-2)_ | - |
| --van-action-bar-height | _50px_ | - |
| --van-action-bar-icon-width | _48px_ | - |
| --van-action-bar-icon-height | _100%_ | - |
@ -163,8 +163,8 @@ The component provides the following CSS variables, which can be used to customi
| --van-action-bar-icon-size | _18px_ | - |
| --van-action-bar-icon-font-size | _var(--van-font-size-xs)_ | - |
| --van-action-bar-icon-active-color | _var(--van-active-color)_ | - |
| --van-action-bar-icon-text-color | _var(--van-gray-7)_ | - |
| --van-action-bar-icon-background-color | _var(--van-background-color-light)_ | - |
| --van-action-bar-icon-text-color | _var(--van-text-color)_ | - |
| --van-action-bar-icon-background | _var(--van-background-2)_ | - |
| --van-action-bar-button-height | _40px_ | - |
| --van-action-bar-button-warning-color | _var(--van-gradient-orange)_ | - |
| --van-action-bar-button-danger-color | _var(--van-gradient-red)_ | - |

View File

@ -32,12 +32,12 @@ app.use(ActionBarButton);
```
```js
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
const onClickIcon = () => Toast('点击图标');
const onClickButton = () => Toast('点击按钮');
const onClickIcon = () => showToast('点击图标');
const onClickButton = () => showToast('点击按钮');
return {
onClickIcon,
onClickButton,
@ -157,18 +157,18 @@ import type {
组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 [ConfigProvider 组件](#/zh-CN/config-provider)。
| 名称 | 默认值 | 描述 |
| --- | --- | --- |
| --van-action-bar-background-color | _var(--van-background-color-light)_ | - |
| --van-action-bar-height | _50px_ | - |
| --van-action-bar-icon-width | _48px_ | - |
| --van-action-bar-icon-height | _100%_ | - |
| --van-action-bar-icon-color | _var(--van-text-color)_ | - |
| --van-action-bar-icon-size | _18px_ | - |
| --van-action-bar-icon-font-size | _var(--van-font-size-xs)_ | - |
| --van-action-bar-icon-active-color | _var(--van-active-color)_ | - |
| --van-action-bar-icon-text-color | _var(--van-gray-7)_ | - |
| --van-action-bar-icon-background-color | _var(--van-background-color-light)_ | - |
| --van-action-bar-button-height | _40px_ | - |
| --van-action-bar-button-warning-color | _var(--van-gradient-orange)_ | - |
| --van-action-bar-button-danger-color | _var(--van-gradient-red)_ | - |
| 名称 | 默认值 | 描述 |
| ------------------------------------- | ---------------------------- | ---- |
| --van-action-bar-background | _var(--van-background-2)_ | - |
| --van-action-bar-height | _50px_ | - |
| --van-action-bar-icon-width | _48px_ | - |
| --van-action-bar-icon-height | _100%_ | - |
| --van-action-bar-icon-color | _var(--van-text-color)_ | - |
| --van-action-bar-icon-size | _18px_ | - |
| --van-action-bar-icon-font-size | _var(--van-font-size-xs)_ | - |
| --van-action-bar-icon-active-color | _var(--van-active-color)_ | - |
| --van-action-bar-icon-text-color | _var(--van-text-color)_ | - |
| --van-action-bar-icon-background | _var(--van-background-2)_ | - |
| --van-action-bar-button-height | _40px_ | - |
| --van-action-bar-button-warning-color | _var(--van-gradient-orange)_ | - |
| --van-action-bar-button-danger-color | _var(--van-gradient-red)_ | - |

View File

@ -3,7 +3,7 @@ import VanActionBar from '..';
import VanActionBarIcon from '../../action-bar-icon';
import VanActionBarButton from '../../action-bar-button';
import { useTranslate } from '../../../docs/site';
import { Toast } from '../../toast';
import { showToast } from '../../toast';
const t = useTranslate({
'zh-CN': {
@ -33,8 +33,8 @@ const t = useTranslate({
customButtonColor: 'Custom Button Color',
},
});
const onClickIcon = () => Toast(t('clickIcon'));
const onClickButton = () => Toast(t('clickButton'));
const onClickIcon = () => showToast(t('clickIcon'));
const onClickButton = () => showToast(t('clickButton'));
</script>
<template>

View File

@ -1,8 +1,6 @@
@import './var.less';
:root {
--van-action-bar-background-color: @action-bar-background-color;
--van-action-bar-height: @action-bar-height;
body {
--van-action-bar-background: var(--van-background-2);
--van-action-bar-height: 50px;
}
.van-action-bar {
@ -14,5 +12,5 @@
align-items: center;
box-sizing: content-box;
height: var(--van-action-bar-height);
background: var(--van-action-bar-background-color);
background: var(--van-action-bar-background);
}

View File

@ -3,6 +3,7 @@ import _ActionBar from './ActionBar';
export const ActionBar = withInstall(_ActionBar);
export default ActionBar;
export { actionBarProps } from './ActionBar';
export type { ActionBarProps } from './ActionBar';
declare module 'vue' {

View File

@ -1,4 +0,0 @@
@import '../style/var.less';
@action-bar-background-color: var(--van-background-color-light);
@action-bar-height: 50px;

View File

@ -29,7 +29,7 @@ export type ActionSheetAction = {
className?: unknown;
};
const actionSheetProps = extend({}, popupSharedProps, {
export const actionSheetProps = extend({}, popupSharedProps, {
title: String,
round: truthProp,
actions: makeArrayProp<ActionSheetAction>(),

View File

@ -29,7 +29,7 @@ Use `actions` prop to set options of action-sheet.
```js
import { ref } from 'vue';
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
@ -41,7 +41,7 @@ export default {
];
const onSelect = (item) => {
show.value = false;
Toast(item.name);
showToast(item.name);
};
return {
@ -67,7 +67,7 @@ export default {
```js
import { ref } from 'vue';
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
@ -77,7 +77,7 @@ export default {
{ name: 'Option 2' },
{ name: 'Option 3' },
];
const onCancel = () => Toast('cancel');
const onCancel = () => showToast('cancel');
return {
show,
@ -247,7 +247,7 @@ The component provides the following CSS variables, which can be used to customi
| --van-action-sheet-description-color | _var(--van-text-color-2)_ | - |
| --van-action-sheet-description-font-size | _var(--van-font-size-md)_ | - |
| --van-action-sheet-description-line-height | _var(--van-line-height-md)_ | - |
| --van-action-sheet-item-background | _var(--van-background-color-light)_ | - |
| --van-action-sheet-item-background | _var(--van-background-2)_ | - |
| --van-action-sheet-item-font-size | _var(--van-font-size-lg)_ | - |
| --van-action-sheet-item-line-height | _var(--van-line-height-lg)_ | - |
| --van-action-sheet-item-text-color | _var(--van-text-color)_ | - |
@ -260,5 +260,5 @@ The component provides the following CSS variables, which can be used to customi
| --van-action-sheet-close-icon-padding | _0 var(--van-padding-md)_ | - |
| --van-action-sheet-cancel-text-color | _var(--van-gray-7)_ | - |
| --van-action-sheet-cancel-padding-top | _var(--van-padding-xs)_ | - |
| --van-action-sheet-cancel-padding-color | _var(--van-background-color)_ | - |
| --van-action-sheet-cancel-padding-color | _var(--van-background)_ | - |
| --van-action-sheet-loading-icon-size | _22px_ | - |

View File

@ -29,7 +29,7 @@ app.use(ActionSheet);
```js
import { ref } from 'vue';
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
@ -43,7 +43,7 @@ export default {
// 默认情况下点击选项时不会自动收起
// 可以通过 close-on-click-action 属性开启自动收起
show.value = false;
Toast(item.name);
showToast(item.name);
};
return {
@ -71,7 +71,7 @@ export default {
```js
import { ref } from 'vue';
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
@ -81,7 +81,7 @@ export default {
{ name: '选项二' },
{ name: '选项三' },
];
const onCancel = () => Toast('取消');
const onCancel = () => showToast('取消');
return {
show,
@ -259,7 +259,7 @@ import type { ActionSheetProps, ActionSheetAction } from 'vant';
| --van-action-sheet-description-color | _var(--van-text-color-2)_ | - |
| --van-action-sheet-description-font-size | _var(--van-font-size-md)_ | - |
| --van-action-sheet-description-line-height | _var(--van-line-height-md)_ | - |
| --van-action-sheet-item-background | _var(--van-background-color-light)_ | - |
| --van-action-sheet-item-background | _var(--van-background-2)_ | - |
| --van-action-sheet-item-font-size | _var(--van-font-size-lg)_ | - |
| --van-action-sheet-item-line-height | _var(--van-line-height-lg)_ | - |
| --van-action-sheet-item-text-color | _var(--van-text-color)_ | - |
@ -272,5 +272,5 @@ import type { ActionSheetProps, ActionSheetAction } from 'vant';
| --van-action-sheet-close-icon-padding | _0 var(--van-padding-md)_ | - |
| --van-action-sheet-cancel-text-color | _var(--van-gray-7)_ | - |
| --van-action-sheet-cancel-padding-top | _var(--van-padding-xs)_ | - |
| --van-action-sheet-cancel-padding-color | _var(--van-background-color)_ | - |
| --van-action-sheet-cancel-padding-color | _var(--van-background)_ | - |
| --van-action-sheet-loading-icon-size | _22px_ | - |

View File

@ -3,7 +3,7 @@ import VanCell from '../../cell';
import VanActionSheet, { ActionSheetAction } from '..';
import { ref, computed } from 'vue';
import { useTranslate } from '../../../docs/site';
import { Toast } from '../../toast';
import { showToast } from '../../toast';
const t = useTranslate({
'zh-CN': {
@ -61,10 +61,10 @@ const actionsWithDescription = computed<ActionSheetAction[]>(() => [
const onSelect = (item: ActionSheetAction) => {
showBasic.value = false;
Toast(item.name);
showToast(item.name);
};
const onCancel = () => Toast(t('cancel'));
const onCancel = () => showToast(t('cancel'));
</script>
<template>

View File

@ -1,28 +1,27 @@
@import './var.less';
@import '../style/mixins/hairline';
:root {
--van-action-sheet-max-height: @action-sheet-max-height;
--van-action-sheet-header-height: @action-sheet-header-height;
--van-action-sheet-header-font-size: @action-sheet-header-font-size;
--van-action-sheet-description-color: @action-sheet-description-color;
--van-action-sheet-description-font-size: @action-sheet-description-font-size;
--van-action-sheet-description-line-height: @action-sheet-description-line-height;
--van-action-sheet-item-background: @action-sheet-item-background;
--van-action-sheet-item-font-size: @action-sheet-item-font-size;
--van-action-sheet-item-line-height: @action-sheet-item-line-height;
--van-action-sheet-item-text-color: @action-sheet-item-text-color;
--van-action-sheet-item-disabled-text-color: @action-sheet-item-disabled-text-color;
--van-action-sheet-subname-color: @action-sheet-subname-color;
--van-action-sheet-subname-font-size: @action-sheet-subname-font-size;
--van-action-sheet-subname-line-height: @action-sheet-subname-line-height;
--van-action-sheet-close-icon-size: @action-sheet-close-icon-size;
--van-action-sheet-close-icon-color: @action-sheet-close-icon-color;
--van-action-sheet-close-icon-padding: @action-sheet-close-icon-padding;
--van-action-sheet-cancel-text-color: @action-sheet-cancel-text-color;
--van-action-sheet-cancel-padding-top: @action-sheet-cancel-padding-top;
--van-action-sheet-cancel-padding-color: @action-sheet-cancel-padding-color;
--van-action-sheet-loading-icon-size: @action-sheet-loading-icon-size;
body {
--van-action-sheet-max-height: 80%;
--van-action-sheet-header-height: 48px;
--van-action-sheet-header-font-size: var(--van-font-size-lg);
--van-action-sheet-description-color: var(--van-text-color-2);
--van-action-sheet-description-font-size: var(--van-font-size-md);
--van-action-sheet-description-line-height: var(--van-line-height-md);
--van-action-sheet-item-background: var(--van-background-2);
--van-action-sheet-item-font-size: var(--van-font-size-lg);
--van-action-sheet-item-line-height: var(--van-line-height-lg);
--van-action-sheet-item-text-color: var(--van-text-color);
--van-action-sheet-item-disabled-text-color: var(--van-text-color-3);
--van-action-sheet-subname-color: var(--van-text-color-2);
--van-action-sheet-subname-font-size: var(--van-font-size-sm);
--van-action-sheet-subname-line-height: var(--van-line-height-sm);
--van-action-sheet-close-icon-size: 22px;
--van-action-sheet-close-icon-color: var(--van-gray-5);
--van-action-sheet-close-icon-padding: 0 var(--van-padding-md);
--van-action-sheet-cancel-text-color: var(--van-gray-7);
--van-action-sheet-cancel-padding-top: var(--van-padding-xs);
--van-action-sheet-cancel-padding-color: var(--van-background);
--van-action-sheet-loading-icon-size: 22px;
}
.van-action-sheet {
@ -95,7 +94,7 @@
&__header {
flex-shrink: 0;
font-weight: var(--van-font-weight-bold);
font-weight: var(--van-font-bold);
font-size: var(--van-action-sheet-header-font-size);
line-height: var(--van-action-sheet-header-height);
text-align: center;

View File

@ -3,6 +3,7 @@ import _ActionSheet from './ActionSheet';
export const ActionSheet = withInstall(_ActionSheet);
export default ActionSheet;
export { actionSheetProps } from './ActionSheet';
export type { ActionSheetProps, ActionSheetAction } from './ActionSheet';
declare module 'vue' {

View File

@ -1,23 +0,0 @@
@import '../style/var.less';
@action-sheet-max-height: 80%;
@action-sheet-header-height: 48px;
@action-sheet-header-font-size: var(--van-font-size-lg);
@action-sheet-description-color: var(--van-text-color-2);
@action-sheet-description-font-size: var(--van-font-size-md);
@action-sheet-description-line-height: var(--van-line-height-md);
@action-sheet-item-background: var(--van-background-color-light);
@action-sheet-item-font-size: var(--van-font-size-lg);
@action-sheet-item-line-height: var(--van-line-height-lg);
@action-sheet-item-text-color: var(--van-text-color);
@action-sheet-item-disabled-text-color: var(--van-text-color-3);
@action-sheet-subname-color: var(--van-text-color-2);
@action-sheet-subname-font-size: var(--van-font-size-sm);
@action-sheet-subname-line-height: var(--van-line-height-sm);
@action-sheet-close-icon-size: 22px;
@action-sheet-close-icon-color: var(--van-gray-5);
@action-sheet-close-icon-padding: 0 var(--van-padding-md);
@action-sheet-cancel-text-color: var(--van-gray-7);
@action-sheet-cancel-padding-top: var(--van-padding-xs);
@action-sheet-cancel-padding-color: var(--van-background-color);
@action-sheet-loading-icon-size: 22px;

View File

@ -25,18 +25,20 @@ import {
import { useExpose } from '../composables/use-expose';
// Components
import { Area, AreaList, AreaColumnOption, AreaInstance } from '../area';
import { Area, AreaList, AreaInstance } from '../area';
import { Cell } from '../cell';
import { Form } from '../form';
import { Field, FieldRule } from '../field';
import { Popup } from '../popup';
import { Toast } from '../toast';
import { showToast } from '../toast';
import { Button } from '../button';
import { Switch } from '../switch';
import AddressEditDetail from './AddressEditDetail';
// Types
import type { AddressEditInfo, AddressEditSearchItem } from './types';
import { PickerConfirmEventParams, PickerOption } from '../picker';
import { AREA_EMPTY_CODE } from '../area/utils';
const [name, bem, t] = createNamespace('address-edit');
@ -49,13 +51,10 @@ const DEFAULT_DATA: AddressEditInfo = {
province: '',
areaCode: '',
isDefault: false,
postalCode: '',
addressDetail: '',
};
const isPostal = (value: string) => /^\d{6}$/.test(value);
const addressEditProps = {
export const addressEditProps = {
areaList: Object as PropType<AreaList>,
isSaving: Boolean,
isDeleting: Boolean,
@ -65,7 +64,6 @@ const addressEditProps = {
showArea: truthProp,
showDetail: truthProp,
showDelete: Boolean,
showPostal: Boolean,
disableArea: Boolean,
searchResult: Array as PropType<AddressEditSearchItem[]>,
telMaxlength: numericProp,
@ -85,10 +83,6 @@ const addressEditProps = {
type: Function as PropType<(val: string) => boolean>,
default: isMobile,
},
postalValidator: {
type: Function as PropType<(val: string) => boolean>,
default: isPostal,
},
};
export type AddressEditProps = ExtractPropTypes<typeof addressEditProps>;
@ -102,11 +96,11 @@ export default defineComponent({
'save',
'focus',
'delete',
'click-area',
'change-area',
'change-detail',
'select-search',
'change-default',
'clickArea',
'changeArea',
'changeDetail',
'selectSearch',
'changeDefault',
],
setup(props, { emit, slots }) {
@ -121,9 +115,9 @@ export default defineComponent({
);
const areaText = computed(() => {
const { country, province, city, county, areaCode } = data;
const { province, city, county, areaCode } = data;
if (areaCode) {
const arr = [country, province, city, county];
const arr = [province, city, county];
if (province && province === city) {
arr.splice(1, 1);
}
@ -137,22 +131,13 @@ export default defineComponent({
() => props.searchResult?.length && detailFocused.value
);
const assignAreaValues = () => {
if (areaRef.value) {
const detail: Record<string, string> = areaRef.value.getArea();
detail.areaCode = detail.code;
delete detail.code;
extend(data, detail);
}
};
const onFocus = (key: string) => {
detailFocused.value = key === 'addressDetail';
emit('focus', key);
};
const rules = computed<Record<string, FieldRule[]>>(() => {
const { validator, telValidator, postalValidator } = props;
const { validator, telValidator } = props;
const makeRule = (name: string, emptyMessage: string): FieldRule => ({
validator: (value) => {
@ -177,10 +162,6 @@ export default defineComponent({
],
areaCode: [makeRule('areaCode', t('areaEmpty'))],
addressDetail: [makeRule('addressDetail', t('addressEmpty'))],
postalCode: [
makeRule('addressDetail', t('postalEmpty')),
{ validator: postalValidator, message: t('postalEmpty') },
],
};
});
@ -188,33 +169,33 @@ export default defineComponent({
const onChangeDetail = (val: string) => {
data.addressDetail = val;
emit('change-detail', val);
emit('changeDetail', val);
};
const onAreaConfirm = (values: AreaColumnOption[]) => {
values = values.filter(Boolean);
const assignAreaText = (options: PickerOption[]) => {
data.province = options[0].text as string;
data.city = options[1].text as string;
data.county = options[2].text as string;
};
if (values.some((value) => !value.code)) {
Toast(t('areaEmpty'));
const onAreaConfirm = ({
selectedValues,
selectedOptions,
}: PickerConfirmEventParams) => {
if (selectedValues.some((value) => value === AREA_EMPTY_CODE)) {
showToast(t('areaEmpty'));
} else {
showAreaPopup.value = false;
assignAreaValues();
emit('change-area', values);
assignAreaText(selectedOptions as PickerOption[]);
emit('changeArea', selectedOptions);
}
};
const onDelete = () => emit('delete', data);
// get values of area component
const getArea = () => areaRef.value?.getValues() || [];
// set area code to area component
const setAreaCode = (code?: string) => {
data.areaCode = code || '';
if (code) {
nextTick(assignAreaValues);
}
};
const onDetailBlur = () => {
@ -234,8 +215,7 @@ export default defineComponent({
'right-icon': () => (
<Switch
v-model={data.isDefault}
size="24"
onChange={(event) => emit('change-default', event)}
onChange={(event) => emit('changeDefault', event)}
/>
),
};
@ -253,21 +233,25 @@ export default defineComponent({
};
useExpose({
getArea,
setAreaCode,
setAddressDetail,
});
watch(
() => props.areaList,
() => setAreaCode(data.areaCode)
);
watch(
() => props.addressInfo,
(value) => {
extend(data, DEFAULT_DATA, value);
setAreaCode(value.areaCode);
nextTick(() => {
const options = areaRef.value?.getSelectedOptions();
if (
options &&
options.every(
(option) => option && option.value !== AREA_EMPTY_CODE
)
) {
assignAreaText(options as PickerOption[]);
}
});
},
{
deep: true,
@ -309,7 +293,7 @@ export default defineComponent({
placeholder={props.areaPlaceholder || t('area')}
onFocus={() => onFocus('areaCode')}
onClick={() => {
emit('click-area');
emit('clickArea');
showAreaPopup.value = !disableArea;
}}
/>
@ -325,20 +309,8 @@ export default defineComponent({
onBlur={onDetailBlur}
onFocus={() => onFocus('addressDetail')}
onInput={onChangeDetail}
onSelect-search={(event: Event) => emit('select-search', event)}
onSelectSearch={(event: Event) => emit('selectSearch', event)}
/>
{props.showPostal && (
<Field
v-show={!hideBottomFields.value}
v-model={data.postalCode}
type="tel"
rules={rules.value.postalCode}
label={t('postal')}
maxlength="6"
placeholder={t('postal')}
onFocus={() => onFocus('postalCode')}
/>
)}
{slots.default?.()}
</div>
{renderSetDefaultCell()}
@ -346,7 +318,7 @@ export default defineComponent({
<Button
block
round
type="danger"
type="primary"
text={props.saveButtonText || t('save')}
class={bem('button')}
loading={props.isSaving}
@ -371,8 +343,8 @@ export default defineComponent({
lazyRender={false}
>
<Area
v-model={data.areaCode}
ref={areaRef}
value={data.areaCode}
loading={!areaListLoaded.value}
areaList={props.areaList}
columnsPlaceholder={props.areaColumnsPlaceholder}

View File

@ -28,7 +28,7 @@ export default defineComponent({
showSearchResult: Boolean,
},
emits: ['blur', 'focus', 'input', 'select-search'],
emits: ['blur', 'focus', 'input', 'selectSearch'],
setup(props, { emit }) {
const field = ref<FieldInstance>();
@ -37,21 +37,10 @@ export default defineComponent({
props.focused && props.searchResult && props.showSearchResult;
const onSelect = (express: AddressEditSearchItem) => {
emit('select-search', express);
emit('selectSearch', express);
emit('input', `${express.address || ''} ${express.name || ''}`.trim());
};
const renderSearchTitle = (express: AddressEditSearchItem) => {
if (express.name) {
const text = express.name.replace(
props.value!,
`<span class=${bem('keyword')}>${props.value}</span>`
);
return <div innerHTML={text} />;
}
};
const renderSearchResult = () => {
if (!showSearchResult()) {
return;
@ -60,12 +49,10 @@ export default defineComponent({
const { searchResult } = props;
return searchResult!.map((express) => (
<Cell
v-slots={{
title: () => renderSearchTitle(express),
}}
clickable
key={express.name + express.address}
key={(express.name || '') + (express.address || '')}
icon="location-o"
title={express.name}
label={express.address}
class={bem('search-item')}
border={false}

View File

@ -23,7 +23,6 @@ app.use(AddressEdit);
```html
<van-address-edit
:area-list="areaList"
show-postal
show-delete
show-set-default
show-search-result
@ -37,14 +36,14 @@ app.use(AddressEdit);
```js
import { ref } from 'vue';
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
const searchResult = ref([]);
const onSave = () => Toast('save');
const onDelete = () => Toast('delete');
const onSave = () => showToast('save');
const onDelete = () => showToast('delete');
const onChangeDetail = (val) => {
if (val) {
searchResult.value = [
@ -80,7 +79,6 @@ export default {
| area-placeholder | placeholder of area input field | _string_ | `Area` |
| address-info | Address Info | _AddressEditInfo_ | `{}` |
| search-result | Address search result | _AddressEditSearchItem[]_ | `[]` |
| show-postal | Whether to show postal field | _boolean_ | `false` |
| show-delete | Whether to show delete button | _boolean_ | `false` |
| show-set-default | Whether to show default address switch | _boolean_ | `false` |
| show-search-result | Whether to show address search result | _boolean_ | `false` |
@ -95,27 +93,26 @@ export default {
| is-deleting | Whether to show delete button loading status | _boolean_ | `false` |
| tel-validator | The method to validate tel | _(tel: string) => boolean_ | - |
| tel-maxlength | Tel maxlength | _number \| string_ | - |
| postal-validator | The method to validate postal | _(tel: string) => boolean_ | - |
| validator | Custom validator | _(key, val) => string_ | - |
### Events
| Event | Description | Arguments |
| --- | --- | --- |
| save | Emitted when the save button is clicked | content: form content |
| focus | Emitted when field is focused | key: field name |
| delete | Emitted when confirming delete | content: form content |
| select-search | Emitted when a search result is selected | value: search content |
| save | Emitted when the save button is clicked | _info: AddressEditInfo_ |
| focus | Emitted when field is focused | _key: string_ |
| delete | Emitted when confirming delete | _info: AddressEditInfo_ |
| select-search | Emitted when a search result is selected | _value: string_ |
| click-area | Emitted when the area field is clicked | - |
| change-area | Emitted when area changed | values: area values |
| change-detail | Emitted when address detail changed | value: address detail |
| change-default | Emitted when switching default address | value: checked |
| change-area | Emitted when area changed | _selectedOptions: PickerOption[]_ |
| change-detail | Emitted when address detail changed | _value: string_ |
| change-default | Emitted when switching default address | _checked: boolean_ |
### Slots
| Name | Description |
| ------- | --------------------------- |
| default | Custom content below postal |
| Name | Description |
| ------- | ----------------------------------- |
| default | Custom content below address detail |
### Methods
@ -160,7 +157,6 @@ addressEditRef.value?.setAddressDetail('');
| county | County | _string_ |
| addressDetail | Detailed Address | _string_ |
| areaCode | Area code | _string_ |
| postalCode | Postal code | _string_ |
| isDefault | Is default address | _boolean_ |
### AddressEditSearchItem Data Structure

View File

@ -23,7 +23,6 @@ app.use(AddressEdit);
```html
<van-address-edit
:area-list="areaList"
show-postal
show-delete
show-set-default
show-search-result
@ -37,14 +36,14 @@ app.use(AddressEdit);
```js
import { ref } from 'vue';
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
const searchResult = ref([]);
const onSave = () => Toast('save');
const onDelete = () => Toast('delete');
const onSave = () => showToast('save');
const onDelete = () => showToast('delete');
const onChangeDetail = (val) => {
if (val) {
searchResult.value = [
@ -80,7 +79,6 @@ export default {
| area-placeholder | 地区输入框占位提示文字 | _string_ | `选择省 / 市 / 区` |
| address-info | 地址信息初始值 | _AddressEditInfo_ | `{}` |
| search-result | 详细地址搜索结果 | _AddressEditSearchItem[]_ | `[]` |
| show-postal | 是否显示邮政编码 | _boolean_ | `false` |
| show-delete | 是否显示删除按钮 | _boolean_ | `false` |
| show-set-default | 是否显示默认地址栏 | _boolean_ | `false` |
| show-search-result | 是否显示搜索结果 | _boolean_ | `false` |
@ -95,21 +93,20 @@ export default {
| is-deleting | 是否显示删除按钮加载动画 | _boolean_ | `false` |
| tel-validator | 手机号格式校验函数 | _string => boolean_ | - |
| tel-maxlength | 手机号最大长度 | _number \| string_ | - |
| postal-validator | 邮政编码格式校验函数 | _string => boolean_ | - |
| validator | 自定义校验函数 | _(key, val) => string_ | - |
### Events
| 事件名 | 说明 | 回调参数 |
| -------------- | -------------------------- | --------------------------- |
| save | 点击保存按钮时触发 | content表单内容 |
| focus | 输入框聚焦时触发 | key: 聚焦的输入框对应的 key |
| delete | 确认删除地址时触发 | content表单内容 |
| select-search | 选中搜索结果时触发 | value: 搜索结果 |
| click-area | 点击收件地区时触发 | - |
| change-area | 修改收件地区时触发 | values: 地区信息 |
| change-detail | 修改详细地址时触发 | value: 详细地址内容 |
| change-default | 切换是否使用默认地址时触发 | value: 是否选中 |
| 事件名 | 说明 | 回调参数 |
| --- | --- | --- |
| save | 点击保存按钮时触发 | _info: AddressEditInfo_ |
| focus | 输入框聚焦时触发 | _key: string_ |
| delete | 确认删除地址时触发 | _info: AddressEditInfo_ |
| select-search | 选中搜索结果时触发 | _value: string_ |
| click-area | 点击收件地区时触发 | - |
| change-area | 修改收件地区时触发 | _selectedOptions: PickerOption[]_ |
| change-detail | 修改详细地址时触发 | _value: string_ |
| change-default | 切换是否使用默认地址时触发 | _checked: boolean_ |
### Slots
@ -162,7 +159,6 @@ addressEditRef.value?.setAddressDetail('');
| county | 区县 | _string_ |
| addressDetail | 详细地址 | _string_ |
| areaCode | 地区编码,通过 [省市区选择](#/zh-CN/area) 获取(必填) | _string_ |
| postalCode | 邮政编码 | _string_ |
| isDefault | 是否为默认地址 | _boolean_ |
### AddressEditSearchItem 数据格式

View File

@ -3,7 +3,7 @@ import VanAddressEdit from '..';
import { ref } from 'vue';
import { areaList } from '@vant/area-data';
import { useTranslate } from '../../../docs/site';
import { Toast } from '../../toast';
import { showToast } from '../../toast';
const t = useTranslate({
'zh-CN': {
@ -41,8 +41,8 @@ const t = useTranslate({
});
const searchResult = ref([]);
const onSave = () => Toast(t('save'));
const onDelete = () => Toast(t('delete'));
const onSave = () => showToast(t('save'));
const onDelete = () => showToast(t('delete'));
const onChangeDetail = (val: string) => {
searchResult.value = val ? t('searchResult') : [];
};
@ -52,7 +52,6 @@ const onChangeDetail = (val: string) => {
<demo-block :title="t('basicUsage')">
<van-address-edit
:area-list="areaList"
show-postal
show-delete
show-set-default
show-search-result

View File

@ -1,10 +1,9 @@
@import './var.less';
:root {
--van-address-edit-padding: @address-edit-padding;
--van-address-edit-buttons-padding: @address-edit-buttons-padding;
--van-address-edit-button-margin-bottom: @address-edit-button-margin-bottom;
--van-address-edit-button-font-size: @address-edit-button-font-size;
body {
--van-address-edit-padding: var(--van-padding-sm);
--van-address-edit-buttons-padding: var(--van-padding-xl)
var(--van-padding-base);
--van-address-edit-button-margin-bottom: var(--van-padding-sm);
--van-address-edit-button-font-size: var(--van-font-size-lg);
}
.van-address-edit {
@ -34,13 +33,7 @@
font-size: var(--van-address-edit-button-font-size);
}
&-detail {
&__search-item {
background: var(--van-gray-2);
}
&__keyword {
color: var(--van-danger-color);
}
&-detail__search-item {
background: var(--van-gray-2);
}
}

View File

@ -3,6 +3,7 @@ import _AddressEdit, { AddressEditProps } from './AddressEdit';
export const AddressEdit = withInstall(_AddressEdit);
export default AddressEdit;
export { addressEditProps } from './AddressEdit';
export type { AddressEditProps };
export type {
AddressEditInfo,

View File

@ -88,25 +88,6 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</div>
</div>
<div class="van-cell van-field">
<div class="van-cell__title van-field__label">
<label id="van-field-label"
for="van-field-input"
>
Postal
</label>
</div>
<div class="van-cell__value van-field__value">
<div class="van-field__body">
<input type="tel"
id="van-field-input"
class="van-field__control"
placeholder="Postal"
aria-labelledby="van-field-label"
>
</div>
</div>
</div>
</div>
<div class="van-cell van-cell--center van-address-edit__default">
<div class="van-cell__title">
@ -116,7 +97,6 @@ exports[`should render demo and match snapshot 1`] = `
</div>
<div role="switch"
class="van-switch"
style="font-size: 24px;"
tabindex="0"
aria-checked="false"
>
@ -126,7 +106,7 @@ exports[`should render demo and match snapshot 1`] = `
</div>
<div class="van-address-edit__buttons">
<button type="submit"
class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-edit__button"
class="van-button van-button--primary van-button--normal van-button--block van-button--round van-address-edit__button"
>
<div class="van-button__content">
<span class="van-button__text">

View File

@ -95,7 +95,7 @@ exports[`should render AddressEdit correctly 1`] = `
</div>
<div class="van-address-edit__buttons">
<button type="submit"
class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-edit__button"
class="van-button van-button--primary van-button--normal van-button--block van-button--round van-address-edit__button"
>
<div class="van-button__content">
<span class="van-button__text">
@ -193,25 +193,6 @@ exports[`should render AddressEdit with props correctly 1`] = `
</div>
</div>
</div>
<div class="van-cell van-field">
<div class="van-cell__title van-field__label">
<label id="van-field-label"
for="van-field-input"
>
Postal
</label>
</div>
<div class="van-cell__value van-field__value">
<div class="van-field__body">
<input type="tel"
id="van-field-input"
class="van-field__control"
placeholder="Postal"
aria-labelledby="van-field-label"
>
</div>
</div>
</div>
</div>
<div class="van-cell van-cell--center van-address-edit__default">
<div class="van-cell__title">
@ -221,7 +202,6 @@ exports[`should render AddressEdit with props correctly 1`] = `
</div>
<div role="switch"
class="van-switch van-switch--on"
style="font-size: 24px;"
tabindex="0"
aria-checked="true"
>
@ -231,7 +211,7 @@ exports[`should render AddressEdit with props correctly 1`] = `
</div>
<div class="van-address-edit__buttons">
<button type="submit"
class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-edit__button"
class="van-button van-button--primary van-button--normal van-button--block van-button--round van-address-edit__button"
>
<div class="van-button__content">
<span class="van-button__text">
@ -292,9 +272,6 @@ exports[`should valid area code and render error message correctly 1`] = `
aria-labelledby="van-field-label"
>
</div>
<div class="van-field__error-message">
Please select a receiving area
</div>
</div>
<i class="van-badge__wrapper van-icon van-icon-arrow van-cell__right-icon">
</i>
@ -326,31 +303,6 @@ exports[`should valid name and render error message correctly 1`] = `
</div>
`;
exports[`should valid postal code and render error message correctly 1`] = `
<div class="van-cell van-field">
<div class="van-cell__title van-field__label">
<label id="van-field-label"
for="van-field-input"
>
Postal
</label>
</div>
<div class="van-cell__value van-field__value">
<div class="van-field__body">
<input type="tel"
id="van-field-input"
class="van-field__control"
placeholder="Postal"
aria-labelledby="van-field-label"
>
</div>
<div class="van-field__error-message">
Wrong postal code
</div>
</div>
</div>
`;
exports[`should valid tel and render error message correctly 1`] = `
<div class="van-cell van-field">
<div class="van-cell__title van-field__label">

View File

@ -1,4 +1,4 @@
import { AddressEdit } from '..';
import { AddressEdit, AddressEditInstance } from '..';
import { areaList } from '../../area/demo/area-simple';
import { mount, later, trigger } from '../../../test';
import { submitForm } from '../../form/test/shared';
@ -11,7 +11,6 @@ const defaultAddressInfo = {
county: '朝阳区',
addressDetail: 'address detail',
areaCode: '110101',
postalCode: '10000',
isDefault: true,
};
@ -23,7 +22,6 @@ const createComponent = (addressInfo = {}) => {
...defaultAddressInfo,
...addressInfo,
},
showPostal: true,
showSetDefault: true,
},
});
@ -45,7 +43,6 @@ test('should render AddressEdit with props correctly', () => {
props: {
areaList,
addressInfo: defaultAddressInfo,
showPostal: true,
showSetDefault: true,
showSearchResult: true,
},
@ -58,7 +55,7 @@ test('should allow to custom validator with validator prop', async () => {
const wrapper = mount(AddressEdit, {
props: {
areaList,
validator: (key, value) => `foo ${key}${value}`,
validator: (key: string, value: string) => `foo ${key}${value}`,
},
});
@ -103,48 +100,13 @@ test('should valid address detail and render error message correctly', async ()
expect(fields[3].html()).toMatchSnapshot();
});
test('should valid postal code and render error message correctly', async () => {
const { fields, wrapper } = createComponent({
postalCode: '123',
});
await submitForm(wrapper);
expect(fields[4].html()).toMatchSnapshot();
});
test('should emit change-detail event after changing address detail', () => {
test('should emit changeDetail event after changing address detail', () => {
const wrapper = mount(AddressEdit);
const field = wrapper.findAll('.van-field__control')[3];
field.element.value = '123';
(field.element as HTMLInputElement).value = '123';
field.trigger('input');
expect(wrapper.emitted('change-detail')[0][0]).toEqual('123');
});
test('should return current areas after calling getArea method', () => {
const wrapper = mount(AddressEdit, {
props: { areaList },
});
expect(wrapper.vm.getArea()).toEqual([
{ code: '110000', name: '北京市' },
{ code: '110100', name: '北京市' },
{ code: '110101', name: '东城区' },
]);
});
test('should update current areas after calling setAreaCode method', async () => {
const wrapper = mount(AddressEdit, {
props: { areaList },
});
wrapper.vm.setAreaCode('110102');
await later();
expect(wrapper.vm.getArea()).toEqual([
{ code: '110000', name: '北京市' },
{ code: '110100', name: '北京市' },
{ code: '110102', name: '西城区' },
]);
expect(wrapper.emitted('changeDetail')).toEqual([['123']]);
});
test('should show search result after focusing to address detail', async () => {
@ -160,25 +122,21 @@ test('should show search result after focusing to address detail', async () => {
});
const field = wrapper.findAll('.van-field__control')[3];
const input = field.element;
const input = field.element as HTMLInputElement;
await field.trigger('focus');
const items = wrapper.findAll('.van-icon-location-o');
items[0].element.parentNode.click();
(items[0].element.parentNode as HTMLElement).click();
await later();
expect(input.value).toEqual('address1 name1');
items[1].element.parentNode.click();
(items[1].element.parentNode as HTMLElement).click();
await later();
expect(input.value).toEqual('name2');
items[2].element.parentNode.click();
(items[2].element.parentNode as HTMLElement).click();
await later();
expect(input.value).toEqual('address2');
await field.trigger('blur');
await later(150);
expect(wrapper.vm.detailFocused).toBeFalsy();
});
test('should emit delete event after clicking the delete button', async () => {
@ -199,12 +157,12 @@ test('should update address detail after calling the setAddressDetail method', a
expect(textarea.element.value).toEqual('address detail');
vm.setAddressDetail('test');
(vm as AddressEditInstance).setAddressDetail('test');
await later();
expect(textarea.element.value).toEqual('test');
});
test('should emit click-area event after clicking the area field', () => {
test('should emit clickArea event after clicking the area field', () => {
const wrapper = mount(AddressEdit, {
props: {
disableArea: true,
@ -213,7 +171,7 @@ test('should emit click-area event after clicking the area field', () => {
const field = wrapper.findAll('.van-field')[2];
field.trigger('click');
expect(wrapper.emitted('click-area')[0]).toBeTruthy();
expect(wrapper.emitted('clickArea')).toHaveLength(1);
});
test('should limit tel maxlength when using tel-maxlength prop', () => {
@ -224,8 +182,9 @@ test('should limit tel maxlength when using tel-maxlength prop', () => {
});
const telInput = wrapper.find('input[type="tel"]');
telInput.element.value = '123456';
const inputEl = telInput.element as HTMLInputElement;
inputEl.value = '123456';
trigger(telInput, 'input');
expect(telInput.element.value).toEqual('1234');
expect(inputEl.value).toEqual('1234');
});

View File

@ -1,10 +1,9 @@
import type { ComponentPublicInstance } from 'vue';
import type { AreaColumnOption } from '../area';
import type { AddressEditProps } from './AddressEdit';
export type AddressEditSearchItem = {
name: string;
address: string;
name?: string;
address?: string;
};
export type AddressEditInfo = {
@ -16,12 +15,10 @@ export type AddressEditInfo = {
province: string;
areaCode: string;
isDefault?: boolean;
postalCode?: string;
addressDetail: string;
};
export type AddressEditExpose = {
getArea: () => AreaColumnOption[];
setAreaCode: (code?: string | undefined) => void;
setAddressDetail: (value: string) => void;
};

View File

@ -1,6 +0,0 @@
@import '../style/var.less';
@address-edit-padding: var(--van-padding-sm);
@address-edit-buttons-padding: var(--van-padding-xl) var(--van-padding-base);
@address-edit-button-margin-bottom: var(--van-padding-sm);
@address-edit-button-font-size: var(--van-font-size-lg);

View File

@ -15,7 +15,7 @@ import AddressListItem, { AddressListAddress } from './AddressListItem';
const [name, bem, t] = createNamespace('address-list');
const addressListProps = {
export const addressListProps = {
list: makeArrayProp<AddressListAddress>(),
modelValue: numericProp,
switchable: truthProp,
@ -36,9 +36,9 @@ export default defineComponent({
'add',
'edit',
'select',
'click-item',
'edit-disabled',
'select-disabled',
'clickItem',
'editDisabled',
'selectDisabled',
'update:modelValue',
],
@ -49,12 +49,12 @@ export default defineComponent({
disabled?: boolean
) => {
const onEdit = () =>
emit(disabled ? 'edit-disabled' : 'edit', item, index);
emit(disabled ? 'editDisabled' : 'edit', item, index);
const onClick = () => emit('click-item', item, index);
const onClick = () => emit('clickItem', item, index);
const onSelect = () => {
emit(disabled ? 'select-disabled' : 'select', item, index);
emit(disabled ? 'selectDisabled' : 'select', item, index);
if (!disabled) {
emit('update:modelValue', item.id);
@ -90,7 +90,7 @@ export default defineComponent({
<Button
round
block
type="danger"
type="primary"
text={props.addButtonText || t('add')}
class={bem('add')}
onClick={() => emit('add')}

View File

@ -62,7 +62,7 @@ export default defineComponent({
}
if (props.address.isDefault && props.defaultTagText) {
return (
<Tag type="danger" round class={bem('tag')}>
<Tag type="primary" round class={bem('tag')}>
{props.defaultTagText}
</Tag>
);
@ -98,11 +98,11 @@ export default defineComponent({
<div class={bem({ disabled })} onClick={onClick}>
<Cell
v-slots={{
value: renderContent,
title: renderContent,
'right-icon': renderRightIcon,
}}
border={false}
valueClass={bem('value')}
titleClass={bem('title')}
/>
{slots.bottom?.(extend({}, props.address, { disabled }))}
</div>

View File

@ -34,7 +34,7 @@ app.use(AddressList);
```js
import { ref } from 'vue';
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
@ -63,8 +63,8 @@ export default {
},
];
const onAdd = () => Toast('Add');
const onEdit = (item, index) => Toast('Edit:' + index);
const onAdd = () => showToast('Add');
const onEdit = (item, index) => showToast('Edit:' + index);
return {
list,
@ -148,5 +148,5 @@ The component provides the following CSS variables, which can be used to customi
| --van-address-list-item-disabled-text-color | _var(--van-text-color-3)_ | - |
| --van-address-list-item-font-size | _13px_ | - |
| --van-address-list-item-line-height | _var(--van-line-height-sm)_ | - |
| --van-address-list-item-radio-icon-color | _var(--van-danger-color)_ | - |
| --van-address-list-radio-color | _var(--van-primary-color)_ | - |
| --van-address-list-edit-icon-size | _20px_ | - |

View File

@ -34,7 +34,7 @@ app.use(AddressList);
```js
import { ref } from 'vue';
import { Toast } from 'vant';
import { showToast } from 'vant';
export default {
setup() {
@ -63,8 +63,8 @@ export default {
},
];
const onAdd = () => Toast('新增地址');
const onEdit = (item, index) => Toast('编辑地址:' + index);
const onAdd = () => showToast('新增地址');
const onEdit = (item, index) => showToast('编辑地址:' + index);
return {
list,
@ -148,5 +148,5 @@ import type { AddressListProps, AddressListAddress } from 'vant';
| --van-address-list-item-disabled-text-color | _var(--van-text-color-3)_ | - |
| --van-address-list-item-font-size | _13px_ | - |
| --van-address-list-item-line-height | _var(--van-line-height-sm)_ | - |
| --van-address-list-item-radio-icon-color | _var(--van-danger-color)_ | - |
| --van-address-list-radio-color | _var(--van-primary-color)_ | - |
| --van-address-list-edit-icon-size | _20px_ | - |

View File

@ -2,7 +2,7 @@
import VanAddressList from '..';
import { ref } from 'vue';
import { useTranslate } from '../../../docs/site';
import { Toast } from '../../toast';
import { showToast } from '../../toast';
const t = useTranslate({
'zh-CN': {
@ -67,10 +67,10 @@ const t = useTranslate({
const chosenAddressId = ref('1');
const onAdd = () => {
Toast(t('add'));
showToast(t('add'));
};
const onEdit = (item: unknown, index: number) => {
Toast(`${t('edit')}:${index}`);
showToast(`${t('edit')}:${index}`);
};
</script>

View File

@ -1,19 +1,17 @@
@import './var.less';
:root {
--van-address-list-padding: @address-list-padding;
--van-address-list-disabled-text-color: @address-list-disabled-text-color;
--van-address-list-disabled-text-padding: @address-list-disabled-text-padding;
--van-address-list-disabled-text-font-size: @address-list-disabled-text-font-size;
--van-address-list-disabled-text-line-height: @address-list-disabled-text-line-height;
--van-address-list-add-button-z-index: @address-list-add-button-z-index;
--van-address-list-item-padding: @address-list-item-padding;
--van-address-list-item-text-color: @address-list-item-text-color;
--van-address-list-item-disabled-text-color: @address-list-item-disabled-text-color;
--van-address-list-item-font-size: @address-list-item-font-size;
--van-address-list-item-line-height: @address-list-item-line-height;
--van-address-list-item-radio-icon-color: @address-list-item-radio-icon-color;
--van-address-list-edit-icon-size: @address-list-edit-icon-size;
body {
--van-address-list-padding: var(--van-padding-sm) var(--van-padding-sm) 80px;
--van-address-list-disabled-text-color: var(--van-text-color-2);
--van-address-list-disabled-text-padding: calc(var(--van-padding-base) * 5) 0;
--van-address-list-disabled-text-font-size: var(--van-font-size-md);
--van-address-list-disabled-text-line-height: var(--van-line-height-md);
--van-address-list-add-button-z-index: 999;
--van-address-list-item-padding: var(--van-padding-sm);
--van-address-list-item-text-color: var(--van-text-color);
--van-address-list-item-disabled-text-color: var(--van-text-color-3);
--van-address-list-item-font-size: 13px;
--van-address-list-item-line-height: var(--van-line-height-sm);
--van-address-list-radio-color: var(--van-primary-color);
--van-address-list-edit-icon-size: 20px;
}
.van-address-list {
@ -30,7 +28,7 @@
width: 100%;
padding-left: var(--van-padding-md);
padding-right: var(--van-padding-md);
background-color: var(--van-background-color-light);
background-color: var(--van-background-2);
}
&__add {
@ -48,14 +46,14 @@
.van-address-item {
padding: var(--van-address-list-item-padding);
background-color: var(--van-background-color-light);
border-radius: var(--van-border-radius-lg);
background-color: var(--van-background-2);
border-radius: var(--van-radius-lg);
&:not(:last-child) {
margin-bottom: var(--van-padding-sm);
}
&__value {
&__title {
padding-right: 44px;
}
@ -106,7 +104,7 @@
}
.van-radio__icon--checked .van-icon {
background-color: var(--van-address-list-item-radio-icon-color);
border-color: var(--van-address-list-item-radio-icon-color);
background-color: var(--van-address-list-radio-color);
border-color: var(--van-address-list-radio-color);
}
}

View File

@ -3,6 +3,7 @@ import _AddressList from './AddressList';
export const AddressList = withInstall(_AddressList);
export default AddressList;
export { addressListProps } from './AddressList';
export type { AddressListProps } from './AddressList';
export type { AddressListAddress } from './AddressListItem';

View File

@ -8,7 +8,7 @@ exports[`should render demo and match snapshot 1`] = `
>
<div class="van-address-item">
<div class="van-cell van-cell--borderless">
<div class="van-cell__value van-cell__value--alone van-address-item__value">
<div class="van-cell__title van-address-item__title">
<div role="radio"
class="van-radio"
tabindex="0"
@ -24,7 +24,7 @@ exports[`should render demo and match snapshot 1`] = `
<div class="van-address-item__name">
John Snow 13000000000
<transition-stub class="van-address-item__tag">
<span class="van-tag van-tag--round van-tag--danger">
<span class="van-tag van-tag--round van-tag--primary">
Default
</span>
</transition-stub>
@ -41,7 +41,7 @@ exports[`should render demo and match snapshot 1`] = `
</div>
<div class="van-address-item">
<div class="van-cell van-cell--borderless">
<div class="van-cell__value van-cell__value--alone van-address-item__value">
<div class="van-cell__title van-address-item__title">
<div role="radio"
class="van-radio"
tabindex="0"
@ -73,7 +73,7 @@ exports[`should render demo and match snapshot 1`] = `
</div>
<div class="van-address-item van-address-item--disabled">
<div class="van-cell van-cell--borderless">
<div class="van-cell__value van-cell__value--alone van-address-item__value">
<div class="van-cell__title van-address-item__title">
<div class="van-address-item__name">
Tywin 1320000000
</div>
@ -87,7 +87,7 @@ exports[`should render demo and match snapshot 1`] = `
</div>
<div class="van-address-list__bottom van-safe-area-bottom">
<button type="button"
class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-list__add"
class="van-button van-button--primary van-button--normal van-button--block van-button--round van-address-list__add"
>
<div class="van-button__content">
<span class="van-button__text">

View File

@ -8,7 +8,7 @@ exports[`should render tag slot correctly 1`] = `
</div>
<div class="van-address-list__bottom van-safe-area-bottom">
<button type="button"
class="van-button van-button--danger van-button--normal van-button--block van-button--round van-address-list__add"
class="van-button van-button--primary van-button--normal van-button--block van-button--round van-address-list__add"
>
<div class="van-button__content">
<span class="van-button__text">

View File

@ -39,7 +39,7 @@ test('should emit select event after clicking radio icon', () => {
expect(wrapper.emitted('select')![0]).toEqual([list[0], 0]);
});
test('should emit click-item event when item is clicked', () => {
test('should emit clickItem event when item is clicked', () => {
const wrapper = mount(AddressList, {
props: {
list,
@ -48,7 +48,7 @@ test('should emit click-item event when item is clicked', () => {
wrapper.find('.van-address-item').trigger('click');
expect(wrapper.emitted('click-item')![0]).toEqual([list[0], 0]);
expect(wrapper.emitted('clickItem')![0]).toEqual([list[0], 0]);
});
test('should render tag slot correctly', () => {

View File

@ -1,16 +0,0 @@
@import '../style/var.less';
@address-list-padding: var(--van-padding-sm) var(--van-padding-sm) 80px;
@address-list-disabled-text-color: var(--van-text-color-2);
@address-list-disabled-text-padding: calc(var(--van-padding-base) * 5) 0
var(--van-padding-md);
@address-list-disabled-text-font-size: var(--van-font-size-md);
@address-list-disabled-text-line-height: var(--van-line-height-md);
@address-list-add-button-z-index: 999;
@address-list-item-padding: var(--van-padding-sm);
@address-list-item-text-color: var(--van-text-color);
@address-list-item-disabled-text-color: var(--van-text-color-3);
@address-list-item-font-size: 13px;
@address-list-item-line-height: var(--van-line-height-sm);
@address-list-item-radio-icon-color: var(--van-danger-color);
@address-list-edit-icon-size: 20px;

View File

@ -2,16 +2,12 @@ import {
ref,
watch,
computed,
reactive,
nextTick,
onMounted,
defineComponent,
type PropType,
type ExtractPropTypes,
} from 'vue';
// Utils
import { deepClone } from '../utils/deep-clone';
import {
pick,
extend,
@ -20,52 +16,28 @@ import {
createNamespace,
} from '../utils';
import { pickerSharedProps } from '../picker/Picker';
import { INHERIT_PROPS, INHERIT_SLOTS, formatDataForCascade } from './utils';
// Composables
import { useExpose } from '../composables/use-expose';
// Components
import { Picker, PickerInstance } from '../picker';
import { Picker, type PickerInstance } from '../picker';
// Types
import type { AreaList, AreaColumnType, AreaColumnOption } from './types';
import type { AreaList } from './types';
import type { PickerExpose } from '../picker/types';
const [name, bem] = createNamespace('area');
const EMPTY_CODE = '000000';
const INHERIT_SLOTS = [
'title',
'cancel',
'confirm',
'toolbar',
'columns-top',
'columns-bottom',
] as const;
const INHERIT_PROPS = [
'title',
'loading',
'readonly',
'itemHeight',
'swipeDuration',
'visibleItemCount',
'cancelButtonText',
'confirmButtonText',
] as const;
const isOverseaCode = (code: string) => code[0] === '9';
const areaProps = extend({}, pickerSharedProps, {
value: String,
export const areaProps = extend({}, pickerSharedProps, {
modelValue: String,
columnsNum: makeNumericProp(3),
columnsPlaceholder: makeArrayProp<string>(),
areaList: {
type: Object as PropType<AreaList>,
default: () => ({}),
},
isOverseaCode: {
type: Function as PropType<(code: string) => boolean>,
default: isOverseaCode,
},
});
export type AreaProps = ExtractPropTypes<typeof areaProps>;
@ -75,264 +47,66 @@ export default defineComponent({
props: areaProps,
emits: ['change', 'confirm', 'cancel'],
emits: ['change', 'confirm', 'cancel', 'update:modelValue'],
setup(props, { emit, slots }) {
const pickerRef = ref<PickerInstance>();
const state = reactive({
code: props.value,
columns: [{ values: [] }, { values: [] }, { values: [] }],
});
const areaList = computed(() => {
const { areaList } = props;
return {
province: areaList.province_list || {},
city: areaList.city_list || {},
county: areaList.county_list || {},
};
});
const placeholderMap = computed(() => {
const { columnsPlaceholder } = props;
return {
province: columnsPlaceholder[0] || '',
city: columnsPlaceholder[1] || '',
county: columnsPlaceholder[2] || '',
};
});
const getDefaultCode = () => {
if (props.columnsPlaceholder.length) {
return EMPTY_CODE;
}
const { county, city } = areaList.value;
const countyCodes = Object.keys(county);
if (countyCodes[0]) {
return countyCodes[0];
}
const cityCodes = Object.keys(city);
if (cityCodes[0]) {
return cityCodes[0];
}
return '';
};
const getColumnValues = (type: AreaColumnType, code?: string) => {
let column: AreaColumnOption[] = [];
if (type !== 'province' && !code) {
return column;
}
const list = areaList.value[type];
column = Object.keys(list).map((listCode) => ({
code: listCode,
name: list[listCode],
}));
if (code) {
// oversea code
if (type === 'city' && props.isOverseaCode(code)) {
code = '9';
}
column = column.filter((item) => item.code.indexOf(code!) === 0);
}
if (placeholderMap.value[type] && column.length) {
// set columns placeholder
let codeFill = '';
if (type === 'city') {
codeFill = EMPTY_CODE.slice(2, 4);
} else if (type === 'county') {
codeFill = EMPTY_CODE.slice(4, 6);
}
column.unshift({
code: code + codeFill,
name: placeholderMap.value[type],
});
}
return column;
};
// get index by code
const getIndex = (type: AreaColumnType, code: string) => {
let compareNum = code.length;
if (type === 'province') {
compareNum = props.isOverseaCode(code) ? 1 : 2;
}
if (type === 'city') {
compareNum = 4;
}
code = code.slice(0, compareNum);
const list = getColumnValues(
type,
compareNum > 2 ? code.slice(0, compareNum - 2) : ''
);
for (let i = 0; i < list.length; i++) {
if (list[i].code.slice(0, compareNum) === code) {
return i;
}
}
return 0;
};
const setValues = () => {
const picker = pickerRef.value;
if (!picker) {
return;
}
let code = state.code || getDefaultCode();
const province = getColumnValues('province');
const city = getColumnValues('city', code.slice(0, 2));
picker.setColumnValues(0, province);
picker.setColumnValues(1, city);
if (
city.length &&
code.slice(2, 4) === '00' &&
!props.isOverseaCode(code)
) {
[{ code }] = city;
}
picker.setColumnValues(2, getColumnValues('county', code.slice(0, 4)));
picker.setIndexes([
getIndex('province', code),
getIndex('city', code),
getIndex('county', code),
]);
};
// parse output columns data
const parseValues = (values: AreaColumnOption[]) =>
values.map((value, index) => {
if (value) {
value = deepClone(value);
if (!value.code || value.name === props.columnsPlaceholder[index]) {
value.code = '';
value.name = '';
}
}
return value;
});
const getValues = () => {
if (pickerRef.value) {
const values = pickerRef.value
.getValues<AreaColumnOption>()
.filter(Boolean);
return parseValues(values);
}
return [];
};
const getArea = () => {
const values = getValues();
const area = {
code: '',
country: '',
province: '',
city: '',
county: '',
};
if (!values.length) {
return area;
}
const names = values.map((item) => item.name);
const validValues = values.filter((value) => value.code);
area.code = validValues.length
? validValues[validValues.length - 1].code
: '';
if (props.isOverseaCode(area.code)) {
area.country = names[1] || '';
area.province = names[2] || '';
} else {
area.province = names[0] || '';
area.city = names[1] || '';
area.county = names[2] || '';
}
return area;
};
const reset = (newCode = '') => {
state.code = newCode;
setValues();
};
const onChange = (values: AreaColumnOption[], index: number) => {
state.code = values[index].code;
setValues();
if (pickerRef.value) {
const parsedValues = parseValues(pickerRef.value.getValues());
emit('change', parsedValues, index);
}
};
const onConfirm = (values: AreaColumnOption[], index: number) => {
setValues();
emit('confirm', parseValues(values), index);
};
const codes = ref<string[]>([]);
const picker = ref<PickerInstance>();
const columns = computed(() => formatDataForCascade(props));
const onChange = (...args: unknown[]) => emit('change', ...args);
const onCancel = (...args: unknown[]) => emit('cancel', ...args);
onMounted(setValues);
const onConfirm = (...args: unknown[]) => emit('confirm', ...args);
watch(
() => props.value,
(value) => {
state.code = value;
setValues();
}
codes,
(newCodes) => {
const lastCode = newCodes.length ? newCodes[newCodes.length - 1] : '';
if (lastCode && lastCode !== props.modelValue) {
emit('update:modelValue', lastCode);
}
},
{ deep: true }
);
watch(() => props.areaList, setValues, { deep: true });
watch(
() => props.columnsNum,
() => {
nextTick(setValues);
}
() => props.modelValue,
(newCode) => {
if (newCode) {
const lastCode = codes.value.length
? codes.value[codes.value.length - 1]
: '';
if (newCode !== lastCode) {
codes.value = [
`${newCode.slice(0, 2)}0000`,
`${newCode.slice(0, 4)}00`,
newCode,
].slice(0, +props.columnsNum);
}
} else {
codes.value = [];
}
},
{ immediate: true }
);
useExpose({ reset, getArea, getValues });
useExpose<PickerExpose>({
confirm: () => picker.value?.confirm(),
getSelectedOptions: () => picker.value?.getSelectedOptions() || [],
});
return () => {
const columns = state.columns.slice(0, +props.columnsNum);
return (
<Picker
v-slots={pick(slots, INHERIT_SLOTS)}
ref={pickerRef}
class={bem()}
columns={columns}
columnsFieldNames={{ text: 'name' }}
onChange={onChange}
onCancel={onCancel}
onConfirm={onConfirm}
{...pick(props, INHERIT_PROPS)}
/>
);
};
return () => (
<Picker
ref={picker}
v-model={codes.value}
v-slots={pick(slots, INHERIT_SLOTS)}
class={bem()}
columns={columns.value}
onChange={onChange}
onCancel={onCancel}
onConfirm={onConfirm}
{...pick(props, INHERIT_PROPS)}
/>
);
},
});

View File

@ -75,12 +75,23 @@ export default {
};
```
### Initial Value
### Model Value
To have a selected value, simply pass the `code` of target area to `value` property.
Bind the currently selected area code via `v-model`.
```html
<van-area title="Title" :area-list="areaList" value="110101" />
<van-area v-model="value" title="Title" :area-list="areaList" />
```
```js
import { ref } from 'vue';
export default {
setup() {
const value = ref('330302');
return { value };
},
};
```
### Columns Number
@ -109,7 +120,7 @@ To have a selected value, simply pass the `code` of target area to `value` prope
| Attribute | Description | Type | Default |
| --- | --- | --- | --- |
| value | the `code` of selected area | _string_ | - |
| v-model | the `code` of selected area | _string_ | - |
| title | Toolbar title | _string_ | - |
| confirm-button-text | Text of confirm button | _string_ | `Confirm` |
| cancel-button-text | Text of cancel button | _string_ | `Cancel` |
@ -117,40 +128,18 @@ To have a selected value, simply pass the `code` of target area to `value` prope
| columns-placeholder | Placeholder of columns | _string[]_ | `[]` |
| loading | Whether to show loading prompt | _boolean_ | `false` |
| readonly | Whether to be readonly | _boolean_ | `false` |
| item-height | Option height, supports `px` `vw` `vh` `rem` unit, default `px` | _number \| string_ | `44` |
| option-height | Option height, supports `px` `vw` `vh` `rem` unit, default `px` | _number \| string_ | `44` |
| columns-num | Level of picker | _number \| string_ | `3` |
| visible-item-count | Count of visible columns | _number \| string_ | `6` |
| visible-option-num | Count of visible columns | _number \| string_ | `6` |
| swipe-duration | Duration of the momentum animation, unit `ms` | _number \| string_ | `1000` |
| is-oversea-code | The method to validate oversea code | _() => boolean_ | - |
### Events
| Event | Description | Arguments |
| --- | --- | --- |
| confirm | Emitted when the confirm button is clicked | _result: ConfirmResult_ |
| cancel | Emitted when the cancel button is clicked | - |
| change | Emitted when current option changed | current: values, column: index |
### ConfirmResult
An array that contains selected area objects.
```js
[
{
code: '330000',
name: 'Zhejiang Province',
},
{
code: '330100',
name: 'Hangzhou',
},
{
code: '330105',
name: 'Xihu District',
},
];
```
| confirm | Emitted when the confirm button is clicked | _{ selectedValues, selectedOptions }_ |
| cancel | Emitted when the cancel button is clicked | _{ selectedValues, selectedOptions }_ |
| change | Emitted when current option is changed | _{ selectedValues, selectedOptions, columnIndex }_ |
### Slots
@ -167,9 +156,10 @@ An array that contains selected area objects.
Use [ref](https://v3.vuejs.org/guide/component-template-refs.html) to get Area instance and call instance methods.
| Name | Description | Attribute | Return value |
| ----- | ------------------------- | --------------- | ------------ |
| reset | Reset all options by code | _code?: string_ | - |
| Name | Description | Attribute | Return value |
| --- | --- | --- | --- |
| confirm | Stop scrolling and emit confirm event | - | - |
| getSelectedOptions | Get current selected options | - | _PickerOption[]_ |
### Types
@ -187,5 +177,5 @@ import type { AreaInstance } from 'vant';
const areaRef = ref<AreaInstance>();
areaRef.value?.reset();
areaRef.value?.confirm();
```

View File

@ -79,12 +79,23 @@ export default {
> Tips: 中国的行政区划每年都会有变动,如果发现省市区数据未及时更新,欢迎提 Pull Request 帮助我们更新。
### 选中省市区
### 控制选中项
如果想选中某个省市区,需要传入一个 `value` 属性,绑定对应的地区码。
通过 `v-model` 绑定当前选中的地区码。
```html
<van-area title="标题" :area-list="areaList" value="110101" />
<van-area v-model="value" title="标题" :area-list="areaList" />
```
```js
import { ref } from 'vue';
export default {
setup() {
const value = ref('330302');
return { value };
},
};
```
### 配置显示列
@ -113,7 +124,7 @@ export default {
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| value | 当前选中项对应的地区码 | _string_ | - |
| v-model | 当前选中项对应的地区码 | _string_ | - |
| title | 顶部栏标题 | _string_ | - |
| confirm-button-text | 确认按钮文字 | _string_ | `确认` |
| cancel-button-text | 取消按钮文字 | _string_ | `取消` |
@ -121,40 +132,18 @@ export default {
| columns-placeholder | 列占位提示文字 | _string[]_ | `[]` |
| loading | 是否显示加载状态 | _boolean_ | `false` |
| readonly | 是否为只读状态,只读状态下无法切换选项 | _boolean_ | `false` |
| item-height | 选项高度,支持 `px` `vw` `vh` `rem` 单位,默认 `px` | _number \| string_ | `44` |
| option-height | 选项高度,支持 `px` `vw` `vh` `rem` 单位,默认 `px` | _number \| string_ | `44` |
| columns-num | 显示列数3-省市区2-省市1-省 | _number \| string_ | `3` |
| visible-item-count | 可见的选项个数 | _number \| string_ | `6` |
| visible-option-num | 可见的选项个数 | _number \| string_ | `6` |
| swipe-duration | 快速滑动时惯性滚动的时长,单位 `ms` | _number \| string_ | `1000` |
| is-oversea-code | 根据地区码校验海外地址,海外地址会划分至单独的分类 | _() => boolean_ | - |
### Events
| 事件 | 说明 | 回调参数 |
| ------- | ------------------ | ------------------------------ |
| confirm | 点击完成按钮时触发 | _result: ConfirmResult_ |
| cancel | 点击取消按钮时触发 | - |
| change | 选项改变时触发 | 所有列选中值,当前列对应的索引 |
### ConfirmResult 格式
confirm 事件返回的数据整体为一个数组,数组每一项对应一列选项中被选中的数据。
```js
[
{
code: '110000',
name: '北京市',
},
{
code: '110100',
name: '北京市',
},
{
code: '110101',
name: '东城区',
},
];
```
| 事件 | 说明 | 回调参数 |
| --- | --- | --- |
| confirm | 点击完成按钮时触发 | _{ selectedValues, selectedOptions }_ |
| cancel | 点击取消按钮时触发 | _{ selectedValues, selectedOptions }_ |
| change | 选项改变时触发 | _{ selectedValues, selectedOptions, columnIndex }_ |
### Slots
@ -173,14 +162,15 @@ confirm 事件返回的数据整体为一个数组,数组每一项对应一列
| 方法名 | 说明 | 参数 | 返回值 |
| --- | --- | --- | --- |
| reset | 根据地区码重置所有选项,若不传地区码,则重置到第一项 | _code?: string_ | - |
| confirm | 停止惯性滚动并触发 `confirm` 事件 | - | - |
| getSelectedOptions | 获取当前选中的选项 | - | _PickerOption[]_ |
### 类型定义
组件导出以下类型定义:
```ts
import type { AreaProps, AreaList, AreaInstance, AreaColumnOption } from 'vant';
import type { AreaProps, AreaList, AreaInstance } from 'vant';
```
`AreaInstance` 是组件实例的类型,用法如下:
@ -191,7 +181,7 @@ import type { AreaInstance } from 'vant';
const areaRef = ref<AreaInstance>();
areaRef.value?.reset();
areaRef.value?.confirm();
```
## 常见问题

View File

@ -41,7 +41,7 @@ export const areaListEn = {
330226: 'Ninghai',
330281: 'Yuyao',
330282: 'Cixi',
330327: 'Cangnan',
330302: 'Lucheng',
330328: 'Wencheng',
330329: 'Shuntai',
330381: 'Ruian',

View File

@ -1 +0,0 @@
// 已迁移至 https://github.com/vant-ui/vant/tree/dev/packages/vant-area-data

View File

@ -7,14 +7,14 @@ import { useTranslate } from '../../../docs/site';
const t = useTranslate({
'zh-CN': {
title2: '选中省市区',
title2: '控制选中项',
title3: '配置显示列',
title4: '配置列占位提示文字',
columnsPlaceholder: ['请选择', '请选择', '请选择'],
areaList,
},
'en-US': {
title2: 'Initial Value',
title2: 'Model Value',
title3: 'Columns Number',
title4: 'Columns Placeholder',
columnsPlaceholder: ['Choose', 'Choose', 'Choose'],
@ -31,7 +31,7 @@ const value = ref('330302');
</demo-block>
<demo-block card :title="t('title2')">
<van-area :title="t('title')" :area-list="t('areaList')" :value="value" />
<van-area v-model="value" :title="t('title')" :area-list="t('areaList')" />
</demo-block>
<demo-block card :title="t('title3')">

View File

@ -3,8 +3,9 @@ import _Area from './Area';
export const Area = withInstall(_Area);
export default Area;
export { areaProps } from './Area';
export type { AreaProps } from './Area';
export type { AreaList, AreaInstance, AreaColumnOption } from './types';
export type { AreaList, AreaInstance } from './types';
declare module 'vue' {
export interface GlobalComponents {

Some files were not shown because too many files have changed in this diff Show More