my_form2.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <template>
  2. <view>
  3. <view class="requiredFieldOnly-head" v-if="requiredFieldOnly">
  4. <view class="label">
  5. 基本信息
  6. </view>
  7. <view class="content">
  8. 仅显示必填信息<text style="padding: 0 2.5px;" /> <u-switch activeColor="#C40C24" v-model="unShowAll" />
  9. </view>
  10. </view>
  11. <slot name="head" />
  12. <view v-for="(item, index) in list " :key="item.key">
  13. <block v-if="item.isMust || !unShowAll">
  14. <!-- 文本输入 -->
  15. <view class="input-box" v-if="item.type == 'text'"
  16. :style="{ marginTop: tovw(item.marginTop || 0), opacity: item.disabled ? 0.7 : 1 }"
  17. @click="focusLabel = item.label">
  18. <view class="box" :style="[{'padding-bottom':item.paddingBottom ? `${tovw(item.paddingBottom)}`:''},{'margin-bottom':item.marginBottom?`${tovw(item.marginBottom)}`:''}]" :class="item.unBorBot ? '' : 'borBot'">
  19. <view class="title" v-if="item.title">{{ item.title }}</view>
  20. <view class="label">
  21. <view class="left">
  22. <text class="must" v-if="item.isMust">*</text>
  23. {{ item.label }}:
  24. </view>
  25. <view class="right">{{ item.descript }}</view>
  26. </view>
  27. <view class="content-box">
  28. <view class="content">
  29. <input v-if="item.inputmode == 'number'" type="number" :disabled="item.disabled"
  30. placeholder-style="color: #BBBBBB;font-family: Source Han Sans SC, Source Han Sans SC;"
  31. :focus="focusLabel == item.label" :placeholder="item.placeholder || '请填写' + item.label"
  32. :value="item.value"
  33. @input="onInput($event, index)" :maxlength="item.maxlength || '499'"
  34. confirm-type="done" />
  35. <input v-else-if="item.inputmode == 'digit'" type="digit" :disabled="item.disabled"
  36. placeholder-style="color: #BBBBBB;font-family: Source Han Sans SC, Source Han Sans SC;"
  37. :focus="focusLabel == item.label" :placeholder="item.placeholder || '请填写' + item.label"
  38. :value="item.value"
  39. @input="onInput($event, index)" :maxlength="item.maxlength || '499'"
  40. confirm-type="done" />
  41. <textarea v-else :disabled="item.disabled"
  42. placeholder-style="color: #BBBBBB;font-family: Source Han Sans SC, Source Han Sans SC;"
  43. auto-height type="text" :focus="focusLabel == item.label"
  44. :placeholder="item.placeholder || '请填写' + item.label" :value="item.value"
  45. @input="onInput($event, index)"
  46. :maxlength="item.maxlength || '499'" confirm-type="done" />
  47. <icon v-if="item.value && !item.disabled" class="icon" type="clear" size="3.733vw"
  48. @click="onClearInput(index)" />
  49. </view>
  50. <view v-if="item.errText" class="err-text">
  51. <icon class="icon" color="#E3041F" type="clear" size="2.733vw" />
  52. {{ item.errText }}
  53. </view>
  54. </view>
  55. </view>
  56. </view>
  57. <!-- 单选 -->
  58. <view class="region" v-else-if="item.type == 'radio'">
  59. <view class="box" :class="item.unBorBot ? '' : 'borBot'">
  60. <view class="label">
  61. <text class="must" v-if="item.isMust">*</text>
  62. {{ item.label }}:
  63. </view>
  64. <u-radio-group v-model="item.value" :placement="item.placement || 'row'"
  65. @change="groupChange($event, index)">
  66. <u-radio v-for="option in item.options"
  67. :customStyle="{ marginRight: tovw(option.marginRight || item.marginRight || 0), marginTop: tovw(option.marginTop || item.marginTop || 0) }"
  68. :key="option.label" :label="option.label" :name="option.name" />
  69. </u-radio-group>
  70. </view>
  71. </view>
  72. </block>
  73. </view>
  74. </view>
  75. </template>
  76. <script>
  77. import { formattedFiles, viewImage } from "../utils/settleFiles.js"
  78. export default {
  79. name: "my_form",
  80. props: {
  81. form: {
  82. type: Array,
  83. default: []
  84. },
  85. isUncomplete: {
  86. type: Function
  87. },
  88. onUploading: {
  89. type: Function
  90. },
  91. requiredFieldOnly: {
  92. type: Boolean
  93. },
  94. isShowAll: {
  95. type: Function
  96. },
  97. interrupt: {
  98. type: Function
  99. }
  100. },
  101. data() {
  102. return {
  103. list: [],
  104. focusLabel: "",
  105. unShowAll: false,
  106. }
  107. },
  108. watch: {
  109. form: {
  110. handler: function (newVal) {
  111. if (newVal) {
  112. this.list = JSON.parse(JSON.stringify(newVal));
  113. setTimeout(() => {
  114. this.verify()
  115. }, 200);
  116. }
  117. },
  118. immediate: true,
  119. },
  120. unShowAll: function (newVal) {
  121. this.$emit("isShowAll", newVal)
  122. }
  123. },
  124. async created() {
  125. },
  126. methods: {
  127. onInput(e, index) {
  128. let item = this.list[index];
  129. item.errText = "";
  130. this.$set(this.list[index], 'value', e.detail.value)
  131. if (item.verify && item.verify.length && item.value != '') {
  132. let err = item.verify.find(r => !new RegExp(r.reg).test(item.value));
  133. if (err) this.$set(this.list[index], 'errText', err.errText)
  134. }
  135. this.verify()
  136. },
  137. setItem(index, item, back = false) {
  138. this.$set(this.list, index, item)
  139. this.verify()
  140. if (back) uni.navigateBack();
  141. },
  142. setValue(index, value, back = false) {
  143. this.$set(this.list[index], 'value', value)
  144. this.verify()
  145. if (back) uni.navigateBack();
  146. },
  147. onClearInput(index) {
  148. let item = this.list[index];
  149. item.errText = ''
  150. this.$set(this.list[index], 'value', '')
  151. this.verify()
  152. },
  153. switchChange(e, index) {
  154. this.$set(this.list[index], 'value', e)
  155. this.verify()
  156. },
  157. verify() {
  158. let list = this.list.filter(v => v.isMust);
  159. let Uncomplete = false;
  160. if (list.length) Uncomplete = list.some(v => {
  161. let res = false;
  162. if (v.type == 'customClass') {
  163. if (v.isMultipleChoice) {
  164. res = v.value.length == 0;
  165. } else {
  166. res = v.value == "";
  167. }
  168. } else if (v.type == 'upload') {
  169. res = v.value.length == 0;
  170. } else if (v.type == 'text') {
  171. res = v.value == "";
  172. if (v.errText) res = true;
  173. } else {
  174. res = v.value == "";
  175. }
  176. return res
  177. })
  178. if (!Uncomplete) Uncomplete = this.list.filter(v => !v.isMust).some(v => v.errText);
  179. this.$emit("isUncomplete", Uncomplete)
  180. },
  181. previewImg(item) {
  182. viewImage(item.url)
  183. },
  184. submit() {
  185. return new Promise((resolve, reject) => {
  186. let res = {};
  187. this.list.forEach(v => {
  188. if (v.type == 'upload') {
  189. res.files = {
  190. temporarys: [],
  191. linksids: [],
  192. }
  193. try {
  194. res.files.temporarys = v.temporarys || []
  195. } catch (error) {
  196. }
  197. try {
  198. res.files.linksids = v.linksids || []
  199. } catch (error) {
  200. }
  201. } else if (v.type == 'region') {
  202. if (v.value.length) {
  203. res.province = v.value[0];
  204. res.city = v.value[1];
  205. res.county = v.value[2];
  206. } else {
  207. res.province = ''
  208. res.city = ''
  209. res.county = ''
  210. }
  211. } else if (v.type == 'switch') {
  212. if (v.isNum) {
  213. res[v.key] = v.value ? 1 : 0;
  214. } else {
  215. res[v.key] = v.value;
  216. }
  217. } else {
  218. try {
  219. res[v.key] = v.value.trim();
  220. } catch (error) {
  221. res[v.key] = v.value;
  222. }
  223. }
  224. })
  225. resolve(res)
  226. })
  227. }
  228. },
  229. }
  230. </script>
  231. <style lang="scss">
  232. .requiredFieldOnly-head {
  233. display: flex;
  234. align-items: center;
  235. justify-content: space-between;
  236. width: 100vw;
  237. height: 45px;
  238. background: #F7F7F7;
  239. padding: 0 10px;
  240. box-sizing: border-box;
  241. .label {
  242. font-family: PingFang SC, PingFang SC;
  243. font-weight: bold;
  244. font-size: 15px;
  245. color: #333333;
  246. line-height: 22px;
  247. }
  248. .content {
  249. display: flex;
  250. align-items: center;
  251. font-family: PingFang SC, PingFang SC;
  252. font-size: 14px;
  253. color: #999999;
  254. }
  255. }
  256. .borBot {
  257. // border-bottom: 1px #DDDDDD solid;
  258. }
  259. .custom-class-box {
  260. width: 100%;
  261. background: #fff;
  262. padding: 15px 0 15px 10px;
  263. box-sizing: border-box;
  264. .head {
  265. width: 355px;
  266. height: 20px;
  267. display: flex;
  268. justify-content: space-between;
  269. align-items: flex-end;
  270. .label {
  271. font-size: 14px;
  272. color: #333333;
  273. line-height: 20px;
  274. }
  275. .state {
  276. font-family: Source Han Sans SC, Source Han Sans SC;
  277. font-size: 12px;
  278. color: #999999;
  279. }
  280. }
  281. .options {
  282. display: flex;
  283. flex-wrap: wrap;
  284. .option {
  285. padding: 6px 10px;
  286. text-align: center;
  287. min-width: 81px;
  288. font-family: PingFang SC, PingFang SC;
  289. font-size: 14px;
  290. color: #333333;
  291. border-radius: 5px;
  292. background: #F2F2F2;
  293. margin-top: 10px;
  294. margin-right: 10px;
  295. box-sizing: border-box;
  296. }
  297. .active {
  298. background: #C30D23;
  299. color: #fff;
  300. }
  301. }
  302. .content {
  303. .file-box {
  304. position: relative;
  305. width: 355px;
  306. height: 240px;
  307. margin-top: 10px;
  308. .video,
  309. .image {
  310. width: 355px;
  311. height: 240px;
  312. border-radius: 5px;
  313. }
  314. .delete {
  315. position: absolute;
  316. width: 30px;
  317. top: 0;
  318. right: 0;
  319. z-index: 1;
  320. }
  321. }
  322. .upload-box {
  323. display: flex;
  324. justify-content: center;
  325. align-items: center;
  326. width: 355px;
  327. height: 45px;
  328. background: #FFFFFF;
  329. border-radius: 5px;
  330. border: 1px dashed #C30D23;
  331. font-family: Source Han Sans SC, Source Han Sans SC;
  332. font-size: 14px;
  333. color: #C30D23;
  334. margin-top: 10px;
  335. }
  336. }
  337. }
  338. .input-box {
  339. width: 100%;
  340. background: #fff;
  341. box-sizing: border-box;
  342. padding: 0 10px;
  343. border-radius: 5px;
  344. .box {
  345. display: flex;
  346. flex-direction: column;
  347. width: 100%;
  348. min-height: 54.4px;
  349. box-sizing: border-box;
  350. border-radius: 8px;
  351. .title {
  352. font-weight: bold;
  353. font-size: 14px;
  354. color: #333333;
  355. margin-top: 16px;
  356. position: relative;
  357. padding-left: 10px;
  358. &::before {
  359. content:'';
  360. width:4px;
  361. height: 16px;
  362. background: #C30D23;
  363. position: absolute;
  364. top: 1px;
  365. left: 0;
  366. }
  367. }
  368. }
  369. .label {
  370. width: 100%;
  371. margin: 14px 0 11px 0;
  372. line-height: 20px;
  373. font-family: Source Han Sans SC, Source Han Sans SC;
  374. font-size: 14px;
  375. color: #666666;
  376. flex-shrink: 0;
  377. display: flex;
  378. justify-content: space-between;
  379. align-items: center;
  380. align-content: baseline;
  381. .right {
  382. font-family: PingFang SC, PingFang SC;
  383. font-weight: 400;
  384. font-size: 12px;
  385. color: #E3041F;
  386. }
  387. .must {
  388. color: #E3041F;
  389. margin-right: 5px;
  390. }
  391. }
  392. .content-box {
  393. .content {
  394. flex: 1;
  395. display: flex;
  396. align-items: center;
  397. box-sizing: border-box;
  398. position: relative;
  399. input,textarea {
  400. width: 100%;
  401. height: 50px !important;
  402. background: #F7F7F7;
  403. border-radius: 8px 8px 8px 8px;
  404. border: 1px solid #DDDDDD;
  405. padding-left: 10px;
  406. }
  407. textarea {
  408. line-height: 50px;
  409. }
  410. .icon {
  411. padding: 5px;
  412. position: absolute;
  413. right: 0;
  414. top: 50%;
  415. transform: translateY(-50%);
  416. z-index: 3;
  417. }
  418. }
  419. .err-text {
  420. font-size: 12px;
  421. color: #E3041F;
  422. margin-bottom: -12px;
  423. .icon {
  424. margin-right: 2px;
  425. }
  426. }
  427. }
  428. }
  429. .region {
  430. width: 100vw;
  431. background: #fff;
  432. box-sizing: border-box;
  433. padding: 15px 0 0 10px;
  434. .box {
  435. display: flex;
  436. padding-bottom: 15px;
  437. padding-right: 10px;
  438. box-sizing: border-box;
  439. .label {
  440. width: 100px;
  441. margin-right: 10px;
  442. line-height: 20px;
  443. font-family: Source Han Sans SC, Source Han Sans SC;
  444. font-size: 14px;
  445. color: #666666;
  446. flex-shrink: 0;
  447. .must {
  448. color: #E3041F;
  449. margin-right: 5px;
  450. }
  451. }
  452. .content-box {
  453. flex: 1;
  454. display: flex;
  455. align-items: center;
  456. justify-content: space-between;
  457. height: 20px;
  458. line-height: 20px;
  459. .placeholder {
  460. font-family: Source Han Sans SC, Source Han Sans SC;
  461. font-size: 14px;
  462. color: #BBBBBB;
  463. }
  464. }
  465. }
  466. }
  467. </style>