form.uvue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <template>
  2. <cl-page>
  3. <view class="p-3">
  4. <demo-item>
  5. <cl-form
  6. :pt="{
  7. className: 'p-2 pb-0'
  8. }"
  9. v-model="formData"
  10. ref="formRef"
  11. :rules="rules"
  12. :disabled="saving"
  13. label-position="top"
  14. >
  15. <cl-form-item prop="avatarUrl">
  16. <cl-upload v-model="formData.avatarUrl" test></cl-upload>
  17. </cl-form-item>
  18. <cl-form-item :label="t('用户名')" prop="nickName" required>
  19. <cl-input
  20. v-model="formData.nickName"
  21. :placeholder="t('请输入用户名')"
  22. clearable
  23. ></cl-input>
  24. </cl-form-item>
  25. <cl-form-item :label="t('邮箱')" prop="email" required>
  26. <cl-input
  27. v-model="formData.email"
  28. :placeholder="t('请输入邮箱地址')"
  29. ></cl-input>
  30. </cl-form-item>
  31. <cl-form-item :label="t('身高')" prop="height" required>
  32. <cl-slider v-model="formData.height" :max="220" show-value>
  33. <template #value="{ value }">
  34. <cl-text
  35. :pt="{
  36. className: 'text-center w-[120rpx]'
  37. }"
  38. >{{ value }} cm</cl-text
  39. >
  40. </template>
  41. </cl-slider>
  42. </cl-form-item>
  43. <cl-form-item :label="t('体重')" prop="weight" required>
  44. <cl-slider v-model="formData.weight" :max="150" show-value>
  45. <template #value="{ value }">
  46. <cl-text
  47. :pt="{
  48. className: 'text-center w-[120rpx]'
  49. }"
  50. >{{ value }} kg</cl-text
  51. >
  52. </template>
  53. </cl-slider>
  54. </cl-form-item>
  55. <cl-form-item :label="t('标签')" prop="tags" required>
  56. <view class="flex flex-row flex-wrap">
  57. <cl-checkbox
  58. v-model="formData.tags"
  59. v-for="(item, index) in tagsOptions"
  60. :key="index"
  61. :value="index"
  62. :pt="{
  63. className: 'mr-5 mt-2'
  64. }"
  65. >{{ item.label }}</cl-checkbox
  66. >
  67. </view>
  68. </cl-form-item>
  69. <cl-form-item :label="t('性别')" prop="gender" required>
  70. <cl-select v-model="formData.gender" :options="genderOptions"></cl-select>
  71. </cl-form-item>
  72. <cl-form-item :label="t('所在地区')" prop="pca" required>
  73. <cl-cascader v-model="formData.pca" :options="pcaOptions"></cl-cascader>
  74. </cl-form-item>
  75. <cl-form-item :label="t('出生年月')" prop="birthday" required>
  76. <cl-select-date v-model="formData.birthday" type="date"></cl-select-date>
  77. </cl-form-item>
  78. <cl-form-item :label="t('个人简介')" prop="description">
  79. <cl-textarea
  80. v-model="formData.description"
  81. :placeholder="t('请输入个人简介')"
  82. :maxlength="200"
  83. ></cl-textarea>
  84. </cl-form-item>
  85. <cl-form-item :label="t('公开状态')">
  86. <cl-switch v-model="formData.isPublic"></cl-switch>
  87. </cl-form-item>
  88. </cl-form>
  89. </demo-item>
  90. <demo-item>
  91. <cl-text pre-wrap :pt="{ className: '!text-sm p-2' }">{{
  92. JSON.stringify(formData, null, 4)
  93. }}</cl-text>
  94. </demo-item>
  95. </view>
  96. <cl-footer>
  97. <view class="flex flex-row">
  98. <cl-button type="info" :pt="{ className: 'flex-1' }" @click="reset">{{
  99. t("重置")
  100. }}</cl-button>
  101. <cl-button
  102. type="primary"
  103. :loading="saving"
  104. :pt="{ className: 'flex-1' }"
  105. @click="submit"
  106. >{{ t("提交") }}</cl-button
  107. >
  108. </view>
  109. </cl-footer>
  110. </cl-page>
  111. </template>
  112. <script setup lang="ts">
  113. import { ref, type Ref } from "vue";
  114. import DemoItem from "../components/item.uvue";
  115. import {
  116. useCascader,
  117. useForm,
  118. useUi,
  119. type ClFormRule,
  120. type ClSelectOption
  121. } from "@/uni_modules/cool-ui";
  122. import pca from "@/data/pca.json";
  123. import { t } from "@/locale";
  124. import { dayUts } from "@/cool";
  125. const ui = useUi();
  126. const { formRef, validate, clearValidate } = useForm();
  127. // 性别选项
  128. const genderOptions = [
  129. {
  130. label: t("未知"),
  131. value: 0
  132. },
  133. {
  134. label: t("男"),
  135. value: 1
  136. },
  137. {
  138. label: t("女"),
  139. value: 2
  140. }
  141. ] as ClSelectOption[];
  142. // 标签选项
  143. const tagsOptions = [
  144. {
  145. label: t("篮球"),
  146. value: 1
  147. },
  148. {
  149. label: t("足球"),
  150. value: 2
  151. },
  152. {
  153. label: t("羽毛球"),
  154. value: 3
  155. },
  156. {
  157. label: t("乒乓球"),
  158. value: 4
  159. },
  160. {
  161. label: t("游泳"),
  162. value: 5
  163. }
  164. ] as ClSelectOption[];
  165. // 地区选项
  166. const pcaOptions = useCascader(pca);
  167. // 自定义表单数据类型
  168. type FormData = {
  169. avatarUrl: string;
  170. nickName: string;
  171. email: string;
  172. height: number;
  173. weight: number;
  174. gender: number;
  175. description: string;
  176. pca: string[];
  177. tags: number[];
  178. birthday: string;
  179. isPublic: boolean;
  180. };
  181. // 表单数据
  182. const formData = ref<FormData>({
  183. avatarUrl: "",
  184. nickName: "神仙都没用",
  185. email: "",
  186. height: 180,
  187. weight: 70,
  188. gender: 0,
  189. description: "",
  190. pca: [],
  191. tags: [1, 2],
  192. birthday: "",
  193. isPublic: false
  194. }) as Ref<FormData>;
  195. // 表单验证规则
  196. const rules = new Map<string, ClFormRule[]>([
  197. [
  198. "nickName",
  199. [
  200. { required: true, message: t("用户名不能为空") },
  201. { min: 3, max: 20, message: t("用户名长度在3-20个字符之间") }
  202. ]
  203. ],
  204. [
  205. "email",
  206. [
  207. { required: true, message: t("邮箱不能为空") },
  208. { pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: t("邮箱格式不正确") }
  209. ]
  210. ],
  211. [
  212. "height",
  213. [
  214. { required: true, message: t("身高不能为空") },
  215. { min: 160, max: 190, message: t("身高在160-190cm之间") }
  216. ]
  217. ],
  218. [
  219. "weight",
  220. [
  221. { required: true, message: t("体重不能为空") },
  222. { min: 40, max: 100, message: t("体重在40-100kg之间") }
  223. ]
  224. ],
  225. [
  226. "tags",
  227. [
  228. { required: true, message: t("标签不能为空") },
  229. { min: 1, max: 2, message: t("标签最多选择2个") }
  230. ]
  231. ],
  232. ["gender", [{ required: true, message: t("性别不能为空") }]],
  233. ["pca", [{ required: true, message: t("所在地区不能为空") }]],
  234. [
  235. "birthday",
  236. [
  237. { required: true, message: t("出生年月不能为空") },
  238. {
  239. validator(value) {
  240. if (dayUts(value).isAfter(dayUts("2010-01-01"))) {
  241. return t("出生年月不大于2010-01-01");
  242. }
  243. return true;
  244. }
  245. }
  246. ]
  247. ]
  248. ]);
  249. // 是否保存中
  250. const saving = ref(false);
  251. // 重置表单数据
  252. function reset() {
  253. formData.value.avatarUrl = "";
  254. formData.value.nickName = "";
  255. formData.value.email = "";
  256. formData.value.height = 180;
  257. formData.value.weight = 70;
  258. formData.value.gender = 0;
  259. formData.value.description = "";
  260. formData.value.pca = [];
  261. formData.value.tags = [];
  262. formData.value.birthday = "";
  263. formData.value.isPublic = false;
  264. clearValidate();
  265. }
  266. // 提交表单
  267. function submit() {
  268. validate((valid, errors) => {
  269. if (valid) {
  270. saving.value = true;
  271. setTimeout(() => {
  272. ui.showToast({
  273. message: t("提交成功"),
  274. icon: "check-line"
  275. });
  276. saving.value = false;
  277. reset();
  278. }, 2000);
  279. } else {
  280. ui.showToast({
  281. message: errors[0].message
  282. });
  283. }
  284. });
  285. }
  286. </script>