index.uvue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. <template>
  2. <view class="form-page">
  3. <!-- 可滚动内容区域 -->
  4. <scroll-view class="form-scroll" scroll-y="true">
  5. <view class="form-container">
  6. <!-- 站点类型 -->
  7. <form-item label="站点类型" :required="formConfig.siteType.required" :error="formConfig.siteType.error">
  8. <select-picker v-model="formData.siteType" placeholder="请选择站点类型" title="选择站点类型" :options="siteTypeOptions" :showLabel="false" />
  9. </form-item>
  10. <!-- 站点名称 -->
  11. <form-item label="站点名称" :required="formConfig.siteName.required" :error="formConfig.siteName.error">
  12. <select-picker v-model="formData.siteName" placeholder="请选择站点名称" title="选择站点名称" :options="siteNameOptions" :showLabel="false" />
  13. </form-item>
  14. <!-- 对比参数 -->
  15. <form-item label="对比参数" :required="formConfig.parameter.required" :error="formConfig.parameter.error">
  16. <select-picker v-model="formData.parameter" placeholder="请选择对比参数" title="选择对比参数" :options="parameterOptions" :showLabel="false" />
  17. </form-item>
  18. <!-- 单行文本 -->
  19. <form-item label="单行文本" :required="formConfig.remark.required" :error="formConfig.remark.error">
  20. <l-input
  21. class="form-input"
  22. v-model="formData.remark"
  23. placeholder="请输入单行文本"
  24. :maxlength="100"
  25. :clearable="true"
  26. :bordered="false"
  27. placeholderStyle="font-size: 14px;"
  28. />
  29. </form-item>
  30. <!-- 多行文本 -->
  31. <form-item label="多行文本" :required="formConfig.multilineText.required" :error="formConfig.multilineText.error" layout="vertical">
  32. <l-textarea
  33. class="form-textarea"
  34. v-model="formData.multilineText"
  35. placeholder="请输入多行文本"
  36. :maxlength="500"
  37. :indicator="true"
  38. :bordered="false"
  39. :autosize="true"
  40. placeholderStyle="font-size: 14px;"
  41. />
  42. </form-item>
  43. <!-- 采集日期 -->
  44. <form-item label="采集日期" :required="formConfig.collectDate.required" :error="formConfig.collectDate.error">
  45. <date-picker v-model="formData.collectDate" placeholder="请选择采集日期" title="选择采集日期" :start="datePickerStart" :end="datePickerEnd" :showLabel="false" />
  46. </form-item>
  47. <!-- 日期范围 -->
  48. <form-item label="日期范围" :required="formConfig.dateRange.required" :error="formConfig.dateRange.error">
  49. <date-range-picker
  50. v-model:startValue="formData.dateRange2Start"
  51. v-model:endValue="formData.dateRange2End"
  52. placeholder="请选择日期范围"
  53. startTitle="选择开始日期"
  54. endTitle="选择结束日期"
  55. :start="datePickerStart"
  56. :end="datePickerEnd"
  57. :showLabel="false"
  58. />
  59. </form-item>
  60. <!-- 上传图片 -->
  61. <form-item label="上传图片" :required="formConfig.images.required" :error="formConfig.images.error" layout="vertical">
  62. <upload-image
  63. :modelValue="formData.images"
  64. @update:modelValue="handleImagesUpdate"
  65. :maxCount="imageMaxCount"
  66. businessType="image"
  67. ></upload-image>
  68. </form-item>
  69. <!-- 上传视频 -->
  70. <form-item label="上传视频" :required="formConfig.videos.required" :error="formConfig.videos.error" layout="vertical">
  71. <upload-video
  72. :modelValue="formData.videos"
  73. @update:modelValue="handleVideosUpdate"
  74. :maxCount="videoMaxCount"
  75. businessType="video"
  76. ></upload-video>
  77. </form-item>
  78. </view>
  79. </scroll-view>
  80. <!-- 固定底部按钮 -->
  81. <view class="footer">
  82. <button class="submit-btn" :disabled="submitting" @click="handleSubmit">
  83. <text class="submit-btn-text">{{ submitting ? "提交中..." : "提交" }}</text>
  84. </button>
  85. </view>
  86. </view>
  87. </template>
  88. <script setup lang="uts">
  89. import { ref } from 'vue'
  90. import type { PickerColumn } from '@/uni_modules/lime-picker'
  91. import type { UploadResponse, VideoItem } from '../../../types/workbench'
  92. // 表单 label 宽度统一配置
  93. const labelWidth = '140rpx'
  94. // 上传组件配置
  95. const imageMaxCount: number = 9
  96. const videoMaxCount: number = 3
  97. // 计算日期范围:当前时间前后100年
  98. const now = new Date()
  99. const currentYear = now.getFullYear()
  100. const startYear = currentYear - 100
  101. const endYear = currentYear + 100
  102. // 格式化日期为 YYYY-MM-DD
  103. const formatDate = (year: number, month: number, day: number): string => {
  104. const m = month < 10 ? `0${month}` : `${month}`
  105. const d = day < 10 ? `0${day}` : `${day}`
  106. return `${year}-${m}-${d}`
  107. }
  108. // 日期选择器的开始和结束日期
  109. const datePickerStart = ref<string>(formatDate(startYear, 1, 1))
  110. const datePickerEnd = ref<string>(formatDate(endYear, 12, 31))
  111. // 表单数据类型
  112. type FormDataType = {
  113. siteType: string
  114. siteName: string
  115. parameter: string
  116. remark: string
  117. multilineText: string
  118. images: UploadResponse[]
  119. videos: VideoItem[]
  120. collectDate: string
  121. dateRange2Start: string
  122. dateRange2End: string
  123. }
  124. // 表单数据
  125. const formData = ref<FormDataType>({
  126. siteType: '',
  127. siteName: '',
  128. parameter: '',
  129. remark: '',
  130. multilineText: '',
  131. images: [],
  132. videos: [],
  133. collectDate: '',
  134. dateRange2Start: '',
  135. dateRange2End: ''
  136. })
  137. const submitting = ref<boolean>(false)
  138. // 表单字段配置类型
  139. type FormFieldConfig = {
  140. required: boolean
  141. error: string
  142. errorMsg: string
  143. }
  144. // 表单字段配置
  145. type FormFieldsConfig = {
  146. siteType: FormFieldConfig
  147. siteName: FormFieldConfig
  148. parameter: FormFieldConfig
  149. remark: FormFieldConfig
  150. multilineText: FormFieldConfig
  151. images: FormFieldConfig
  152. videos: FormFieldConfig
  153. collectDate: FormFieldConfig
  154. dateRange: FormFieldConfig
  155. }
  156. // 表单配置
  157. const formConfig = ref<FormFieldsConfig>({
  158. siteType: {
  159. required: true,
  160. error: '',
  161. errorMsg: '请选择站点类型'
  162. },
  163. siteName: {
  164. required: true,
  165. error: '',
  166. errorMsg: '请选择站点名称'
  167. },
  168. parameter: {
  169. required: false,
  170. error: '',
  171. errorMsg: '请选择对比参数'
  172. },
  173. remark: {
  174. required: false,
  175. error: '',
  176. errorMsg: '请输入单行文本'
  177. },
  178. multilineText: {
  179. required: false,
  180. error: '',
  181. errorMsg: '请输入多行文本'
  182. },
  183. images: {
  184. required: false,
  185. error: '',
  186. errorMsg: '请上传图片'
  187. },
  188. videos: {
  189. required: false,
  190. error: '',
  191. errorMsg: '请上传视频'
  192. },
  193. collectDate: {
  194. required: false,
  195. error: '',
  196. errorMsg: '请选择采集日期'
  197. },
  198. dateRange: {
  199. required: false,
  200. error: '',
  201. errorMsg: '请选择完整的日期范围'
  202. }
  203. })
  204. // 清空所有错误信息
  205. const clearErrors = (): void => {
  206. formConfig.value.siteType.error = ''
  207. formConfig.value.siteName.error = ''
  208. formConfig.value.parameter.error = ''
  209. formConfig.value.remark.error = ''
  210. formConfig.value.multilineText.error = ''
  211. formConfig.value.images.error = ''
  212. formConfig.value.videos.error = ''
  213. formConfig.value.collectDate.error = ''
  214. formConfig.value.dateRange.error = ''
  215. }
  216. // 站点类型选项
  217. const siteTypeOptions = [
  218. { label: '人工站', value: '人工站' },
  219. { label: '自动站', value: '自动站' },
  220. { label: '水文站', value: '水文站' },
  221. { label: '气象站', value: '气象站' }
  222. ] as PickerColumn
  223. // 站点名称选项
  224. const siteNameOptions = [
  225. { label: '肖川-龙口', value: '肖川-龙口' },
  226. { label: '北京-朝阳', value: '北京-朝阳' },
  227. { label: '上海-浦东', value: '上海-浦东' },
  228. { label: '深圳-南山', value: '深圳-南山' }
  229. ] as PickerColumn
  230. // 对比参数选项
  231. const parameterOptions = [
  232. { label: '水温(℃)', value: '水温(℃)' },
  233. { label: '水位(m)', value: '水位(m)' },
  234. { label: '流量(m³/s)', value: '流量(m³/s)' },
  235. { label: '降雨量(mm)', value: '降雨量(mm)' }
  236. ] as PickerColumn
  237. // 表单验证
  238. const validateForm = (): boolean => {
  239. // 清空之前的错误信息
  240. clearErrors()
  241. let isValid = true
  242. // 站点类型验证
  243. if (formConfig.value.siteType.required && formData.value.siteType.length == 0) {
  244. formConfig.value.siteType.error = formConfig.value.siteType.errorMsg
  245. isValid = false
  246. }
  247. // 站点名称验证
  248. if (formConfig.value.siteName.required && formData.value.siteName.length == 0) {
  249. formConfig.value.siteName.error = formConfig.value.siteName.errorMsg
  250. isValid = false
  251. }
  252. // 对比参数验证
  253. if (formConfig.value.parameter.required && formData.value.parameter.length == 0) {
  254. formConfig.value.parameter.error = formConfig.value.parameter.errorMsg
  255. isValid = false
  256. }
  257. // 单行文本验证
  258. if (formConfig.value.remark.required && formData.value.remark.length == 0) {
  259. formConfig.value.remark.error = formConfig.value.remark.errorMsg
  260. isValid = false
  261. }
  262. // 多行文本验证
  263. if (formConfig.value.multilineText.required && formData.value.multilineText.length == 0) {
  264. formConfig.value.multilineText.error = formConfig.value.multilineText.errorMsg
  265. isValid = false
  266. }
  267. // 图片验证
  268. if (formConfig.value.images.required && formData.value.images.length == 0) {
  269. formConfig.value.images.error = formConfig.value.images.errorMsg
  270. isValid = false
  271. }
  272. // 视频验证
  273. if (formConfig.value.videos.required && formData.value.videos.length == 0) {
  274. formConfig.value.videos.error = formConfig.value.videos.errorMsg
  275. isValid = false
  276. }
  277. // 采集日期验证
  278. if (formConfig.value.collectDate.required && formData.value.collectDate.length == 0) {
  279. formConfig.value.collectDate.error = formConfig.value.collectDate.errorMsg
  280. isValid = false
  281. }
  282. // 日期范围验证
  283. if (formConfig.value.dateRange.required && (formData.value.dateRange2Start.length == 0 || formData.value.dateRange2End.length == 0)) {
  284. formConfig.value.dateRange.error = formConfig.value.dateRange.errorMsg
  285. isValid = false
  286. }
  287. return isValid
  288. }
  289. // 处理图片更新
  290. const handleImagesUpdate = (images: UploadResponse[]): void => {
  291. formData.value.images = images
  292. }
  293. // 处理视频更新
  294. const handleVideosUpdate = (videos: VideoItem[]): void => {
  295. formData.value.videos = videos
  296. }
  297. // 提交表单
  298. const handleSubmit = async (): Promise<void> => {
  299. if (!validateForm()) {
  300. return
  301. }
  302. try {
  303. submitting.value = true
  304. // 打印表单数据
  305. console.log('表单数据:', formData.value)
  306. // 模拟提交
  307. await new Promise<void>((resolve) => {
  308. setTimeout(() => {
  309. resolve()
  310. }, 1500)
  311. })
  312. uni.showToast({
  313. title: '提交成功',
  314. icon: 'success'
  315. })
  316. // 延迟返回
  317. // setTimeout(() => {
  318. // uni.navigateBack()
  319. // }, 1000)
  320. } catch (e: any) {
  321. uni.showToast({
  322. title: e.message ?? '提交失败',
  323. icon: 'none'
  324. })
  325. } finally {
  326. submitting.value = false
  327. }
  328. }
  329. </script>
  330. <style lang="scss">
  331. @import "@/static/css/form.scss";
  332. .form-page {
  333. flex: 1;
  334. background-color: #e8f0f9;
  335. flex-direction: column;
  336. .form-scroll {
  337. flex: 1;
  338. padding: 30rpx;
  339. padding-bottom: 20rpx;
  340. .form-container {
  341. background-color: #ffffff;
  342. border-radius: 16rpx;
  343. padding: 30rpx;
  344. }
  345. }
  346. .footer {
  347. padding: 20rpx 30rpx;
  348. padding-bottom: 40rpx;
  349. .submit-btn {
  350. width: 100%;
  351. height: 88rpx;
  352. background-color: #007aff;
  353. border-radius: 12rpx;
  354. color: #ffffff !important;
  355. &:disabled {
  356. background-color: #cccccc;
  357. }
  358. .submit-btn-text {
  359. font-size: 32rpx;
  360. color: #ffffff !important;
  361. font-weight: bold;
  362. }
  363. }
  364. }
  365. }
  366. </style>