addLand.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. <template>
  2. <view class="page-container">
  3. <!-- 基本信息区域 -->
  4. <view class="section">
  5. <view class="section-title">基本信息</view>
  6. <view class="form-card">
  7. <view class="form-item">
  8. <view class="item-label">变更理由<text class="required">*</text></view>
  9. <picker :range="dictData.bgly" :range-key="'label'"
  10. :value="getDictIndex('bgly', editForm.changeReason)" @change="handleDictChange('bgly', $event)">
  11. <view class="item-picker">
  12. {{ getDictLabel('bgly', editForm.changeReason) || '请选择' }}
  13. <text class="picker-arrow">▼</text>
  14. </view>
  15. </picker>
  16. </view>
  17. <view class="form-item">
  18. <view class="item-label">地块名称<text class="required">*</text></view>
  19. <input class="item-input" v-model="editForm.dkmc" placeholder="请输入"
  20. placeholder-class="input-placeholder" />
  21. </view>
  22. <view class="form-item">
  23. <view class="item-label">确权面积(合同)<text class="required">*</text></view>
  24. <input class="item-input" v-model="editForm.htmj" placeholder="请输入"
  25. placeholder-class="input-placeholder" type="digit" />
  26. <view class="unit">亩</view>
  27. </view>
  28. <view class="form-item">
  29. <view class="item-label">所有权性质<text class="required">*</text></view>
  30. <picker :range="dictData.syqxz" :range-key="'label'" :value="getDictIndex('syqxz', editForm.syqxz)"
  31. @change="handleDictChange('syqxz', $event)">
  32. <view class="item-picker">
  33. {{ getDictLabel('syqxz', editForm.syqxz) || '请选择' }}
  34. <text class="picker-arrow">▼</text>
  35. </view>
  36. </picker>
  37. </view>
  38. <view class="form-item">
  39. <view class="item-label">地块类别<text class="required">*</text></view>
  40. <picker :range="dictData.dklb" :range-key="'label'" :value="getDictIndex('dklb', editForm.dklb)"
  41. @change="handleDictChange('dklb', $event)">
  42. <view class="item-picker">
  43. {{ getDictLabel('dklb', editForm.dklb) || '请选择' }}
  44. <text class="picker-arrow">▼</text>
  45. </view>
  46. </picker>
  47. </view>
  48. <view class="form-item">
  49. <view class="item-label">利用类型<text class="required">*</text></view>
  50. <picker :range="dictData.lylx" :range-key="'label'" :value="getDictIndex('lylx', editForm.tdlylx)"
  51. @change="handleDictChange('lylx', $event)">
  52. <view class="item-picker">
  53. {{ getDictLabel('lylx', editForm.tdlylx) || '请选择' }}
  54. <text class="picker-arrow">▼</text>
  55. </view>
  56. </picker>
  57. </view>
  58. <view class="form-item">
  59. <view class="item-label">地力等级<text class="required">*</text></view>
  60. <picker :range="dictData.dldj" :range-key="'label'" :value="getDictIndex('dldj', editForm.dldj)"
  61. @change="handleDictChange('dldj', $event)">
  62. <view class="item-picker">
  63. {{ getDictLabel('dldj', editForm.dldj) || '请选择' }}
  64. <text class="picker-arrow">▼</text>
  65. </view>
  66. </picker>
  67. </view>
  68. <view class="form-item">
  69. <view class="item-label">土地用途<text class="required">*</text></view>
  70. <picker :range="dictData.tdyt" :range-key="'label'" :value="getDictIndex('tdyt', editForm.tdyt)"
  71. @change="handleDictChange('tdyt', $event)">
  72. <view class="item-picker">
  73. {{ getDictLabel('tdyt', editForm.tdyt) || '请选择' }}
  74. <text class="picker-arrow">▼</text>
  75. </view>
  76. </picker>
  77. </view>
  78. <view class="form-item">
  79. <view class="item-label">基本农田<text class="required">*</text></view>
  80. <picker :range="dictData.jbnt" :range-key="'label'" :value="getDictIndex('jbnt', editForm.sfjbnt)"
  81. @change="handleDictChange('jbnt', $event)">
  82. <view class="item-picker">
  83. {{ getDictLabel('jbnt', editForm.sfjbnt) || '请选择' }}
  84. <text class="picker-arrow">▼</text>
  85. </view>
  86. </picker>
  87. </view>
  88. <view class="form-item">
  89. <view class="item-label">地块东至<text class="required">*</text></view>
  90. <input class="item-input" v-model="editForm.dkdz" placeholder="请输入"
  91. placeholder-class="input-placeholder" />
  92. </view>
  93. <view class="form-item">
  94. <view class="item-label">地块西至<text class="required">*</text></view>
  95. <input class="item-input" v-model="editForm.dkxz" placeholder="请输入"
  96. placeholder-class="input-placeholder" />
  97. </view>
  98. <view class="form-item">
  99. <view class="item-label">地块南至<text class="required">*</text></view>
  100. <input class="item-input" v-model="editForm.dknz" placeholder="请输入"
  101. placeholder-class="input-placeholder" />
  102. </view>
  103. <view class="form-item">
  104. <view class="item-label">地块北至<text class="required">*</text></view>
  105. <input class="item-input" v-model="editForm.dkbz" placeholder="请输入"
  106. placeholder-class="input-placeholder" />
  107. </view>
  108. <!-- <view class="form-item">
  109. <view class="text-[30rpx] text-[#666]">范围选择</view>
  110. <view class="text-[30rpx] text-[#333]" @click="handleChooseRange">在地图上打点框选大致范围</view>
  111. </view> -->
  112. <view class="form-item form-item-textarea">
  113. <view class="item-label">备注信息</view>
  114. <textarea class="item-textarea" v-model="editForm.remark" placeholder="请输入"
  115. placeholder-class="input-placeholder" />
  116. </view>
  117. </view>
  118. </view>
  119. <!-- 图片信息区域(和家庭成员完全一样结构) -->
  120. <view class="section">
  121. <view class="section-title">图片信息</view>
  122. <view class="form-card">
  123. <!-- 现场指界 -->
  124. <view class="upload-item">
  125. <view class="upload-title">现场指界</view>
  126. <view class="upload-wrap">
  127. <view class="upload-box" v-for="(img, idx) in imageList.xczj.img" :key="idx">
  128. <image :src="img" class="upload-img" mode="aspectFill" />
  129. <view class="del-btn" @click.stop="delImg('xczj', idx)">×</view>
  130. </view>
  131. <view class="upload-box add-box" @click="handleUploadImage('xczj')">
  132. <view class="upload-plus">+</view>
  133. </view>
  134. </view>
  135. </view>
  136. <!-- 附件资料 -->
  137. <view class="upload-item">
  138. <view class="upload-title required">*附件资料</view>
  139. <view class="upload-wrap">
  140. <view class="upload-box" v-for="(img, idx) in imageList.fjzl.img" :key="idx">
  141. <image :src="img" class="upload-img" mode="aspectFill" />
  142. <view class="del-btn" @click.stop="delImg('fjzl', idx)">×</view>
  143. </view>
  144. <view class="upload-box add-box" @click="handleUploadImage('fjzl')">
  145. <view class="upload-plus">+</view>
  146. </view>
  147. </view>
  148. </view>
  149. </view>
  150. </view>
  151. <view class="submit-btn" @click="handleSubmit">新增</view>
  152. </view>
  153. </template>
  154. <script setup lang="ts">
  155. import { ref, computed } from 'vue';
  156. import { onLoad,onShow } from '@dcloudio/uni-app';
  157. import { uploadFile } from '@/utils/upload.js';
  158. import { getDictData, updateLandInfo } from '@/api/farmerApi.js';
  159. // ==================== 类型定义(完全匹配后端字段) ====================
  160. interface DictItem {
  161. value : string;
  162. label : string;
  163. }
  164. interface DictData {
  165. bgly : DictItem[];
  166. syqxz : DictItem[];
  167. dklb : DictItem[];
  168. lylx : DictItem[];
  169. dldj : DictItem[];
  170. tdyt : DictItem[];
  171. jbnt : DictItem[];
  172. }
  173. // 后端 dkTemp 完整字段结构
  174. interface DkTemp {
  175. auditOpinion : string;
  176. bizStatus : number;
  177. bsm : number;
  178. changeReason : string;
  179. dkbm : string;
  180. dkbz : string;
  181. dkbzxx : string;
  182. dkdz : string;
  183. dklb : string;
  184. dkmc : string;
  185. dknz : string;
  186. dkxz : string;
  187. dldj : string;
  188. kjzb : string;
  189. remark : string;
  190. scmj : number;
  191. htmj : number;
  192. sfjbnt : string;
  193. status : number;
  194. syqxz : string;
  195. tdlylx : string;
  196. tdyt : string;
  197. ysdm : string;
  198. zjrxm : string;
  199. }
  200. interface EditForm {
  201. // 页面绑定字段(映射后端)
  202. changeReason : string;
  203. dkmc : string;
  204. htmj : string;
  205. syqxz : string;
  206. dklb : string;
  207. tdlylx : string;
  208. dldj : string;
  209. tdyt : string;
  210. sfjbnt : string;
  211. dkdz : string;
  212. dkxz : string;
  213. dknz : string;
  214. dkbz : string;
  215. remark : string;
  216. }
  217. interface ImageItem {
  218. img : string[];
  219. number : string[];
  220. }
  221. interface ImageList {
  222. xczj : ImageItem;
  223. fjzl : ImageItem;
  224. }
  225. type DictType = keyof DictData;
  226. // ==================== 响应式数据 ====================
  227. const rowData = ref<any>({});
  228. const addCbfbm = ref<any>({});
  229. const fbfbmcd = ref<any>({});
  230. const dictData = ref<DictData>({
  231. bgly: [], syqxz: [], dklb: [], lylx: [], dldj: [], tdyt: [], jbnt: []
  232. });
  233. const editForm = ref<EditForm>({
  234. changeReason: '',
  235. dkmc: '',
  236. htmj: '',
  237. syqxz: '',
  238. dklb: '',
  239. tdlylx: '',
  240. dldj: '',
  241. tdyt: '',
  242. sfjbnt: '',
  243. dkdz: '',
  244. dkxz: '',
  245. dknz: '',
  246. dkbz: '',
  247. remark: ''
  248. });
  249. const coordinateData = ref<Array>([])
  250. // ✅ 和家庭成员完全一样的图片结构
  251. const imageList = ref<ImageList>({
  252. xczj: { img: [], number: [] },
  253. fjzl: { img: [], number: [] }
  254. });
  255. // ==================== 字典工具函数 ====================
  256. const transformDictFormat = (originalDict : Record<string | number, string>) : DictItem[] => {
  257. if (!originalDict || typeof originalDict !== 'object') return [];
  258. return Object.entries(originalDict).map(([key, value]) => ({
  259. value: String(key),
  260. label: value as string
  261. }));
  262. };
  263. const getDictLabel = (dictType : DictType, value : string | number | undefined) : string => {
  264. if (!value || !dictData.value[dictType]?.length) return '';
  265. const item = dictData.value[dictType].find(item => String(item.value) === String(value));
  266. return item?.label || '';
  267. };
  268. const getDictIndex = computed(() => {
  269. return (dictType : DictType, value : string) => {
  270. if (!value || !dictData.value[dictType].length) return 0;
  271. return dictData.value[dictType].findIndex(item => String(item.value) === String(value));
  272. };
  273. });
  274. const handleDictChange = (dictType : DictType, e : any) => {
  275. const list = dictData.value[dictType];
  276. if (!list.length) return;
  277. // 字段映射:根据字典类型对应到 editForm 字段
  278. if (dictType === 'bgly') {
  279. editForm.value.changeReason = list[e.detail.value]?.value || '';
  280. } else if (dictType === 'lylx') {
  281. editForm.value.tdlylx = list[e.detail.value]?.value || '';
  282. } else if (dictType === 'jbnt') {
  283. editForm.value.sfjbnt = list[e.detail.value]?.value || '';
  284. } else {
  285. editForm.value[dictType as keyof EditForm] = list[e.detail.value]?.value || '';
  286. }
  287. };
  288. // ==================== 字典加载 ====================
  289. const loadDictData = async () => {
  290. try {
  291. const [bgly, syqxz, dklb, lylx, dldj, tdyt, jbnt] = await Promise.all([
  292. getDictData('dic_bgly'), getDictData('dic_syqxz'), getDictData('dic_dklb'),
  293. getDictData('dic_tdlylx'), getDictData('dic_dldj'), getDictData('dic_tdyt'), getDictData('dic_sf')
  294. ]);
  295. dictData.value.bgly = transformDictFormat(bgly.data);
  296. dictData.value.syqxz = transformDictFormat(syqxz.data);
  297. dictData.value.dklb = transformDictFormat(dklb.data);
  298. dictData.value.lylx = transformDictFormat(lylx.data);
  299. dictData.value.dldj = transformDictFormat(dldj.data);
  300. dictData.value.tdyt = transformDictFormat(tdyt.data);
  301. dictData.value.jbnt = transformDictFormat(jbnt.data);
  302. } catch (err) {
  303. uni.showToast({ title: '字典加载失败', icon: 'none' });
  304. }
  305. };
  306. // ==================== ✅ 完全和家庭成员一样的上传方法 ====================
  307. const handleUploadImage = async (type : keyof ImageList) => {
  308. try {
  309. const chooseRes = await uni.chooseImage({ count: 1, sizeType: ['compressed'] });
  310. const filePath = chooseRes.tempFilePaths[0];
  311. uni.showLoading({ title: '上传中...' });
  312. const result = await uploadFile(filePath);
  313. if (result.code === 200 && result.data.id) {
  314. imageList.value[type].img.push(filePath);
  315. imageList.value[type].number.push(result.data.id);
  316. uni.showToast({ title: '上传成功', icon: 'success' });
  317. } else {
  318. uni.showToast({ title: '上传失败', icon: 'none' });
  319. }
  320. } catch (err) {
  321. uni.showToast({ title: '取消上传', icon: 'none' });
  322. } finally {
  323. uni.hideLoading();
  324. }
  325. };
  326. // ==================== ✅ 删除:和家庭成员完全一样 ====================
  327. const delImg = (type : keyof ImageList, index : number) => {
  328. imageList.value[type].img.splice(index, 1);
  329. imageList.value[type].number.splice(index, 1);
  330. };
  331. // ==================== 初始化回显(支持旧字段名自动映射) ====================
  332. const initEditForm = () => {
  333. if (!rowData.value || !rowData.value.dkTemp) return;
  334. const dkTemp = rowData.value.dkTemp;
  335. // 回填页面字段
  336. editForm.value = {
  337. changeReason: dkTemp.changeReason || '',
  338. dkmc: dkTemp.dkmc || '',
  339. htmj: dkTemp.htmj || '',
  340. syqxz: dkTemp.syqxz || '',
  341. dklb: dkTemp.dklb || '',
  342. tdlylx: dkTemp.tdlylx || '',
  343. dldj: dkTemp.dldj || '',
  344. tdyt: dkTemp.tdyt || '',
  345. sfjbnt: dkTemp.sfjbnt || '',
  346. dkdz: dkTemp.dkdz || '',
  347. dkxz: dkTemp.dkxz || '',
  348. dknz: dkTemp.dknz || '',
  349. dkbz: dkTemp.dkbz || '',
  350. remark: dkTemp.remark || ''
  351. };
  352. // 图片回显
  353. imageList.value.xczj.img = rowData.value.xczjUrl ? [rowData.value.xczjUrl] : [];
  354. imageList.value.xczj.number = rowData.value.attachmentxczjFileIds || [];
  355. imageList.value.fjzl.img = rowData.value.fjzlUrl ? [rowData.value.fjzlUrl] : [];
  356. imageList.value.fjzl.number = rowData.value.attachmentfjzlFileIds || [];
  357. };
  358. // ==================== 表单校验 ====================
  359. const validateForm = () : boolean => {
  360. const { changeReason, dkmc, htmj, syqxz, dklb, tdlylx, dldj, tdyt, sfjbnt, dkdz, dkxz, dknz, dkbz } = editForm.value;
  361. if (!changeReason) { uni.showToast({ title: '请选择变更理由', icon: 'none' }); return false; }
  362. if (!dkmc?.trim()) { uni.showToast({ title: '请输入地块名称', icon: 'none' }); return false; }
  363. if (!htmj?.trim()) { uni.showToast({ title: '请输入确权面积', icon: 'none' }); return false; }
  364. if (!syqxz) { uni.showToast({ title: '请选择所有权性质', icon: 'none' }); return false; }
  365. if (!dklb) { uni.showToast({ title: '请选择地块类别', icon: 'none' }); return false; }
  366. if (!tdlylx) { uni.showToast({ title: '请选择利用类型', icon: 'none' }); return false; }
  367. if (!dldj) { uni.showToast({ title: '请选择地力等级', icon: 'none' }); return false; }
  368. if (!tdyt) { uni.showToast({ title: '请选择土地用途', icon: 'none' }); return false; }
  369. if (!sfjbnt) { uni.showToast({ title: '请选择基本农田', icon: 'none' }); return false; }
  370. if (!dkdz?.trim()) { uni.showToast({ title: '请输入东至', icon: 'none' }); return false; }
  371. if (!dkxz?.trim()) { uni.showToast({ title: '请输入西至', icon: 'none' }); return false; }
  372. if (!dknz?.trim()) { uni.showToast({ title: '请输入南至', icon: 'none' }); return false; }
  373. if (!dkbz?.trim()) { uni.showToast({ title: '请输入北至', icon: 'none' }); return false; }
  374. if (imageList.value.fjzl.number.length === 0) { uni.showToast({ title: '请上传附件资料', icon: 'none' }); return false; }
  375. return true;
  376. };
  377. // ==================== 提交(完全匹配后端结构,空值自动补全) ====================
  378. const handleSubmit = async () => {
  379. if (!validateForm()) return;
  380. // 1. 构建 dkTemp 对象,完全匹配后端字段,未填写的自动补空/0
  381. const dkTemp : DkTemp = {
  382. auditOpinion: '', // 页面无输入,固定空
  383. bizStatus: '', // 页面无输入,固定0
  384. bsm: '', // 页面无输入,固定0
  385. changeReason: editForm.value.changeReason || '',
  386. dkbm: fbfbmcd.value, // 页面无输入,固定空
  387. dkbz: editForm.value.dkbz || '',
  388. dkbzxx: '', // 页面无输入,固定空
  389. dkdz: editForm.value.dkdz || '',
  390. dklb: editForm.value.dklb || '',
  391. dkmc: editForm.value.dkmc || '',
  392. dknz: editForm.value.dknz || '',
  393. dkxz: editForm.value.dkxz || '',
  394. dldj: editForm.value.dldj || '',
  395. kjzb: '', // 页面无输入,固定空
  396. remark: editForm.value.remark || '',
  397. scmj: '', // 页面无输入,固定0
  398. htmj: Number(editForm.value.htmj) || '', // 字符串转数字,空则0
  399. sfjbnt: editForm.value.sfjbnt || '',
  400. status: '', // 页面无输入,固定0
  401. syqxz: editForm.value.syqxz || '',
  402. tdlylx: editForm.value.tdlylx || '',
  403. tdyt: editForm.value.tdyt || '',
  404. ysdm: '', // 页面无输入,固定空
  405. zjrxm: '' // 页面无输入,固定空
  406. };
  407. // 2. 构建完整提交数据,完全匹配后端结构
  408. const submitData = {
  409. dkTemp,
  410. attachmentxczjFileIds: imageList.value.xczj.number || [],
  411. attachmentfjzlFileIds: imageList.value.fjzl.number || [],
  412. // geoJson: [coordinateData.value] || [],
  413. geoJson: [],
  414. cbdkxxTemp: {
  415. cbfbm: addCbfbm.value,
  416. fbfbm: fbfbmcd.value
  417. }
  418. };
  419. uni.showLoading({ title: '提交中...' });
  420. try {
  421. const res = await updateLandInfo(submitData);
  422. if (res.code === 200) {
  423. uni.showToast({ title: '添加成功', icon: 'success' });
  424. setTimeout(() => {
  425. uni.navigateBack({
  426. success: () => {
  427. const pages = getCurrentPages();
  428. const prevPage = pages[pages.length - 1];
  429. if (prevPage && typeof prevPage.onLoad === 'function') {
  430. prevPage.onLoad(prevPage.options);
  431. }
  432. }
  433. });
  434. }, 1500);
  435. } else {
  436. uni.showToast({ title: res.msg || '提交失败', icon: 'none' });
  437. }
  438. } catch (err) {
  439. uni.showToast({ title: '提交失败', icon: 'none' });
  440. } finally {
  441. uni.hideLoading();
  442. }
  443. };
  444. const handleChooseRange = () => {
  445. uni.navigateTo({ url: `/pages/map/map?memberInfo=${encodeURIComponent(JSON.stringify(addCbfbm.value))}` })
  446. };
  447. // ==================== 入口 ====================
  448. onLoad(async (options) => {
  449. fbfbmcd.value = options.cbfbm.substring(0, 14);
  450. addCbfbm.value = options.cbfbm;
  451. await loadDictData();
  452. initEditForm();
  453. });
  454. // addLand.vue
  455. onShow(() => {
  456. const pages = getCurrentPages()
  457. const currentPage = pages[pages.length - 1]
  458. // 接收从 map.vue 回来的数据
  459. if (currentPage.$vm.selectedLandData) {
  460. // console.log('✅ addLand 收到地图返回的数据:', currentPage.$vm.selectedLandData)
  461. // 你可以直接赋值给表单
  462. coordinateData.value = currentPage.$vm.selectedLandData.points
  463. // console.log('收到地图返回的数据',coordinateData.value)
  464. // 用完清空,防止重复
  465. currentPage.$vm.selectedLandData = null
  466. }
  467. })
  468. </script>
  469. <style lang="scss" scoped>
  470. .page-container {
  471. padding: 20rpx;
  472. padding-bottom: 40rpx;
  473. background-color: #f5faff;
  474. }
  475. .section {
  476. margin-bottom: 40rpx;
  477. }
  478. .section-title {
  479. font-size: 36rpx;
  480. font-weight: bold;
  481. color: #333;
  482. margin-bottom: 20rpx;
  483. padding-left: 10rpx;
  484. }
  485. .form-card {
  486. background-color: #fff;
  487. border-radius: 24rpx;
  488. padding: 30rpx;
  489. box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
  490. }
  491. .form-item {
  492. display: flex;
  493. align-items: center;
  494. justify-content: space-between;
  495. padding: 30rpx 0;
  496. border-bottom: 1rpx solid #f0f0f0;
  497. &.form-item-textarea {
  498. align-items: flex-start;
  499. padding-bottom: 0;
  500. border-bottom: none;
  501. flex-direction: column;
  502. }
  503. }
  504. .item-label {
  505. font-size: 32rpx;
  506. color: #333;
  507. flex-shrink: 0;
  508. }
  509. .required {
  510. color: #f5222d;
  511. margin-left: 4rpx;
  512. }
  513. .item-input {
  514. flex: 1;
  515. text-align: right;
  516. font-size: 32rpx;
  517. color: #333;
  518. height: 40rpx;
  519. line-height: 40rpx;
  520. &::placeholder {
  521. color: #c9cdd4;
  522. }
  523. }
  524. .input-placeholder {
  525. color: #c9cdd4;
  526. font-size: 32rpx;
  527. }
  528. .item-picker {
  529. flex: 1;
  530. display: flex;
  531. align-items: center;
  532. justify-content: flex-end;
  533. font-size: 32rpx;
  534. color: #333;
  535. height: 40rpx;
  536. line-height: 40rpx;
  537. }
  538. .picker-arrow {
  539. margin-left: 10rpx;
  540. font-size: 28rpx;
  541. color: #333;
  542. }
  543. .item-textarea {
  544. width: 100%;
  545. min-height: 200rpx;
  546. font-size: 32rpx;
  547. color: #333;
  548. border: 1rpx solid #e5e6eb;
  549. border-radius: 12rpx;
  550. padding: 20rpx;
  551. box-sizing: border-box;
  552. margin-top: 20rpx;
  553. }
  554. .unit {
  555. margin-left: 10rpx;
  556. font-size: 28rpx;
  557. color: #666;
  558. }
  559. .upload-item {
  560. margin-bottom: 40rpx;
  561. }
  562. .upload-title {
  563. font-size: 32rpx;
  564. color: #333;
  565. margin-bottom: 20rpx;
  566. line-height: 1.5;
  567. &.required::before {
  568. content: "*";
  569. color: #f5222d;
  570. margin-right: 4rpx;
  571. }
  572. }
  573. .upload-wrap {
  574. display: flex;
  575. flex-wrap: wrap;
  576. gap: 20rpx;
  577. align-items: center;
  578. }
  579. .upload-box {
  580. width: 280rpx;
  581. height: 280rpx;
  582. background-color: #f5f5f5;
  583. border-radius: 8rpx;
  584. display: flex;
  585. align-items: center;
  586. justify-content: center;
  587. overflow: hidden;
  588. position: relative;
  589. }
  590. .upload-plus {
  591. font-size: 80rpx;
  592. color: #333;
  593. font-weight: 300;
  594. }
  595. .upload-img {
  596. width: 100%;
  597. height: 100%;
  598. }
  599. .add-box {
  600. border: 2rpx dashed #ccc;
  601. }
  602. .del-btn {
  603. position: absolute;
  604. top: 0;
  605. right: 0;
  606. width: 50rpx;
  607. height: 50rpx;
  608. background: rgba(255, 0, 0, 0.7);
  609. color: #fff;
  610. font-size: 30rpx;
  611. display: flex;
  612. align-items: center;
  613. justify-content: center;
  614. }
  615. .submit-btn {
  616. width: 80%;
  617. height: 90rpx;
  618. background-color: #409eff;
  619. color: #fff;
  620. font-size: 36rpx;
  621. border-radius: 45rpx;
  622. display: flex;
  623. align-items: center;
  624. justify-content: center;
  625. margin: 0 auto;
  626. box-shadow: 0 4rpx 12rpx rgba(64, 158, 255, 0.3);
  627. }
  628. </style>