demo.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <template>
  2. <div class="demo full">
  3. <h2>基本用法</h2>
  4. <nut-form>
  5. <nut-form-item label="姓名">
  6. <input class="nut-input-text" placeholder="请输入姓名" type="text" />
  7. </nut-form-item>
  8. <nut-form-item label="年龄">
  9. <input class="nut-input-text" placeholder="请输入年龄" type="text" />
  10. </nut-form-item>
  11. <nut-form-item label="联系电话">
  12. <input class="nut-input-text" placeholder="请输入联系电话" type="text" />
  13. </nut-form-item>
  14. <nut-form-item label="地址">
  15. <input class="nut-input-text" placeholder="请输入地址" type="text" />
  16. </nut-form-item>
  17. <nut-form-item label="备注">
  18. <nut-textarea placeholder="请输入备注" type="text" />
  19. </nut-form-item>
  20. </nut-form>
  21. <h2>动态表单</h2>
  22. <nut-form :model-value="dynamicForm.state" ref="dynamicRefForm">
  23. <nut-form-item label="姓名" prop="name" required :rules="[{ required: true, message: '请填写姓名' }]">
  24. <input class="nut-input-text" v-model="dynamicForm.state.name" placeholder="请输入姓名" type="text" />
  25. </nut-form-item>
  26. <nut-form-item
  27. :label="'联系方式' + index"
  28. :prop="'tels.' + index + '.value'"
  29. required
  30. :rules="[{ required: true, message: '请填写联系方式' + index }]"
  31. :key="item.key"
  32. v-for="(item, index) in dynamicForm.state.tels"
  33. >
  34. <input class="nut-input-text" v-model="item.value" :placeholder="'请输入联系方式' + index" type="text" />
  35. </nut-form-item>
  36. <nut-cell>
  37. <nut-button size="small" style="margin-right: 10px" @click="dynamicForm.methods.add">添加</nut-button>
  38. <nut-button size="small" style="margin-right: 10px" @click="dynamicForm.methods.remove">删除</nut-button>
  39. <nut-button type="primary" size="small" @click="dynamicForm.methods.submit">提交</nut-button>
  40. </nut-cell>
  41. </nut-form>
  42. <h2>表单校验</h2>
  43. <nut-form :model-value="formData" ref="ruleForm">
  44. <nut-form-item label="姓名" prop="name" required :rules="[{ required: true, message: '请填写姓名' }]">
  45. <input
  46. class="nut-input-text"
  47. @blur="customBlurValidate('name')"
  48. v-model="formData.name"
  49. placeholder="请输入姓名,blur 事件校验"
  50. type="text"
  51. />
  52. </nut-form-item>
  53. <nut-form-item
  54. label="年龄"
  55. prop="age"
  56. required
  57. :rules="[
  58. { required: true, message: '请填写年龄' },
  59. { validator: customValidator, message: '必须输入数字' },
  60. { regex: /^(\d{1,2}|1\d{2}|200)$/, message: '必须输入0-200区间' }
  61. ]"
  62. >
  63. <input
  64. class="nut-input-text"
  65. v-model="formData.age"
  66. placeholder="请输入年龄,必须数字且0-200区间"
  67. type="text"
  68. />
  69. </nut-form-item>
  70. <nut-form-item
  71. label="联系电话"
  72. prop="tel"
  73. required
  74. :rules="[
  75. { required: true, message: '请填写联系电话' },
  76. { validator: asyncValidator, message: '电话格式不正确' }
  77. ]"
  78. >
  79. <input
  80. class="nut-input-text"
  81. v-model="formData.tel"
  82. placeholder="请输入联系电话,异步校验电话格式"
  83. type="text"
  84. />
  85. </nut-form-item>
  86. <nut-form-item label="地址" prop="address" required :rules="[{ required: true, message: '请填写地址' }]">
  87. <input class="nut-input-text" v-model="formData.address" placeholder="请输入地址" type="text" />
  88. </nut-form-item>
  89. <nut-cell>
  90. <nut-button type="primary" size="small" style="margin-right: 10px" @click="submit">提交</nut-button>
  91. <nut-button size="small" @click="reset">重置提示状态</nut-button>
  92. </nut-cell>
  93. </nut-form>
  94. <h2>表单类型</h2>
  95. <nut-form>
  96. <nut-form-item label="开关">
  97. <nut-switch v-model="formData2.switch"></nut-switch>
  98. </nut-form-item>
  99. <nut-form-item label="复选框">
  100. <nut-checkbox v-model="formData2.checkbox">复选框</nut-checkbox>
  101. </nut-form-item>
  102. <nut-form-item label="单选按钮">
  103. <nut-radiogroup direction="horizontal" v-model="formData2.radio">
  104. <nut-radio label="1">选项1</nut-radio>
  105. <nut-radio disabled label="2">选项2</nut-radio>
  106. <nut-radio label="3">选项3</nut-radio>
  107. </nut-radiogroup>
  108. </nut-form-item>
  109. <nut-form-item label="评分">
  110. <nut-rate v-model="formData2.rate" />
  111. </nut-form-item>
  112. <nut-form-item label="步进器">
  113. <nut-inputnumber v-model="formData2.number" />
  114. </nut-form-item>
  115. <nut-form-item label="滑块">
  116. <nut-range hidden-tag v-model="formData2.range"></nut-range>
  117. </nut-form-item>
  118. <nut-form-item label="文件上传">
  119. <nut-uploader url="http://服务地址" v-model:file-list="formData2.defaultFileList" maximum="3" multiple>
  120. </nut-uploader>
  121. </nut-form-item>
  122. <nut-form-item label="地址">
  123. <input
  124. class="nut-input-text"
  125. v-model="formData2.address"
  126. @click="addressModule.methods.show"
  127. readonly
  128. placeholder="请选择地址"
  129. type="text"
  130. />
  131. <!-- nut-address -->
  132. <nut-address
  133. v-model:visible="addressModule.state.show"
  134. :province="addressModule.state.province"
  135. :city="addressModule.state.city"
  136. :country="addressModule.state.country"
  137. :town="addressModule.state.town"
  138. @change="addressModule.methods.onChange"
  139. custom-address-title="请选择所在地区"
  140. ></nut-address>
  141. </nut-form-item>
  142. </nut-form>
  143. </div>
  144. </template>
  145. <script lang="ts">
  146. import { Toast } from '@/packages/nutui.vue';
  147. import { reactive, ref } from 'vue';
  148. import { createComponent } from '../../utils/create';
  149. const { createDemo } = createComponent('form');
  150. export default createDemo({
  151. props: {},
  152. setup() {
  153. const formData = reactive({
  154. name: '',
  155. age: '',
  156. tel: '',
  157. address: ''
  158. });
  159. const dynamicRefForm = ref<any>(null);
  160. const dynamicForm = {
  161. state: reactive({
  162. name: '',
  163. tels: new Array({
  164. key: 1,
  165. value: ''
  166. })
  167. }),
  168. methods: {
  169. submit() {
  170. dynamicRefForm.value.validate().then(({ valid, errors }: any) => {
  171. if (valid) {
  172. console.log('success', dynamicForm);
  173. } else {
  174. console.log('error submit!!', errors);
  175. }
  176. });
  177. },
  178. remove() {
  179. dynamicForm.state.tels.splice(dynamicForm.state.tels.length - 1, 1);
  180. },
  181. add() {
  182. let newIndex = dynamicForm.state.tels.length;
  183. dynamicForm.state.tels.push({
  184. key: Date.now(),
  185. value: ''
  186. });
  187. }
  188. }
  189. };
  190. const validate = (item: any) => {
  191. console.log(item);
  192. };
  193. const formData2 = reactive({
  194. switch: false,
  195. checkbox: false,
  196. radio: 0,
  197. number: 0,
  198. rate: 3,
  199. range: 30,
  200. address: '',
  201. defaultFileList: [
  202. {
  203. name: '文件1.png',
  204. url: 'https://m.360buyimg.com/babel/jfs/t1/164410/22/25162/93384/616eac6cE6c711350/0cac53c1b82e1b05.gif',
  205. status: 'success',
  206. message: '上传成功',
  207. type: 'image'
  208. },
  209. {
  210. name: '文件2.png',
  211. url: 'https://m.360buyimg.com/babel/jfs/t1/164410/22/25162/93384/616eac6cE6c711350/0cac53c1b82e1b05.gif',
  212. status: 'uploading',
  213. message: '上传中...',
  214. type: 'image'
  215. }
  216. ]
  217. });
  218. const addressModule = reactive({
  219. state: {
  220. show: false,
  221. province: [
  222. { id: 1, name: '北京' },
  223. { id: 2, name: '广西' },
  224. { id: 3, name: '江西' },
  225. { id: 4, name: '四川' }
  226. ],
  227. city: [
  228. { id: 7, name: '朝阳区' },
  229. { id: 8, name: '崇文区' },
  230. { id: 9, name: '昌平区' },
  231. { id: 6, name: '石景山区' }
  232. ],
  233. country: [
  234. { id: 3, name: '八里庄街道' },
  235. { id: 9, name: '北苑' },
  236. { id: 4, name: '常营乡' }
  237. ],
  238. town: []
  239. },
  240. methods: {
  241. show() {
  242. addressModule.state.show = !addressModule.state.show;
  243. if (addressModule.state.show) {
  244. formData2.address = '';
  245. }
  246. },
  247. onChange({ custom, next, value }: any) {
  248. formData2.address += value.name;
  249. const name = addressModule.state[next];
  250. if (name.length < 1) {
  251. addressModule.state.show = false;
  252. }
  253. }
  254. }
  255. });
  256. const ruleForm = ref<any>(null);
  257. const submit = () => {
  258. ruleForm.value.validate().then(({ valid, errors }: any) => {
  259. if (valid) {
  260. console.log('success', formData);
  261. } else {
  262. console.log('error submit!!', errors);
  263. }
  264. });
  265. };
  266. const reset = () => {
  267. ruleForm.value.reset();
  268. };
  269. // 失去焦点校验
  270. const customBlurValidate = (prop: string) => {
  271. ruleForm.value.validate(prop).then(({ valid, errors }: any) => {
  272. if (valid) {
  273. console.log('success', formData);
  274. } else {
  275. console.log('error submit!!', errors);
  276. }
  277. });
  278. };
  279. // 函数校验
  280. const customValidator = (val: string) => /^\d+$/.test(val);
  281. // Promise 异步校验
  282. const asyncValidator = (val: string) => {
  283. return new Promise((resolve) => {
  284. Toast.loading('模拟异步验证中...');
  285. setTimeout(() => {
  286. Toast.hide();
  287. resolve(/^400(-?)[0-9]{7}$|^1\d{10}$|^0[0-9]{2,3}-[0-9]{7,8}$/.test(val));
  288. }, 1000);
  289. });
  290. };
  291. return {
  292. ruleForm,
  293. formData,
  294. validate,
  295. customValidator,
  296. asyncValidator,
  297. customBlurValidate,
  298. submit,
  299. reset,
  300. formData2,
  301. addressModule,
  302. dynamicForm,
  303. dynamicRefForm
  304. };
  305. }
  306. });
  307. </script>
  308. <style lang="scss" scoped></style>