Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. <template>
  2. <!-- 表单 -->
  3. <form @submit="formSubmit">
  4. <view v-for="(item, index) in formData" :key="index">
  5. <view v-show="!item.show">
  6. <!-- 是否显示-->
  7. <view
  8. style="min-height: 90rpx"
  9. :class="
  10. item.vertical === 2 ? 'as-layout-vertical' : 'as-layout-horizontal'
  11. "
  12. >
  13. <!-- 标题 -->
  14. <view
  15. class="as-gravity-center-start"
  16. :style="'min-width:' + config.titleWidth + 'rpx'"
  17. style="
  18. margin: 0 20rpx;
  19. flex-direction: row;
  20. display: flex;
  21. flex-direction: row;
  22. "
  23. >
  24. <image
  25. class="img-size"
  26. :src="item.required ? `${$imgUrl}must.png` : ''"
  27. mode="aspectFill"
  28. ></image>
  29. <view
  30. :class="item.vertical === 2 ? 'text-left' : 'text-justify'"
  31. class="text-title"
  32. style="width: 100%"
  33. >
  34. {{ item.title }}
  35. </view>
  36. </view>
  37. <!-- 内容 -->
  38. <view class="as-weight as-gravity-center-start">
  39. <!-- 文本内容 -->
  40. <view v-if="item.type === 1" class="text" :style="item.style">{{
  41. item[item.value]
  42. }}</view>
  43. <!-- 输入框 -->
  44. <view v-if="item.type === 2" style="width: 100%">
  45. <input
  46. :disabled="item.disabled"
  47. v-model="item[item.value]"
  48. :type="item.inputType"
  49. :placeholder="item.hint ? item.hint : '请输入' + item.title"
  50. placeholder-class="text-hint"
  51. class="text as-gravity-center-start"
  52. :maxlength="item.maxlength"
  53. :style="item.style"
  54. style="min-height: 90rpx; word-break: break-all"
  55. />
  56. </view>
  57. <!-- 多项选择器 -->
  58. <view v-if="item.type === 3">
  59. <checkbox-group
  60. @change="checkboxChange($event, item)"
  61. :disabled="item.disabled"
  62. >
  63. <view class="as-layout-horizontal">
  64. <label
  65. style="
  66. display: flex;
  67. flex-direction: row;
  68. margin-right: 30rpx;
  69. "
  70. v-for="(itemData, index) in item.itemData"
  71. :key="index"
  72. >
  73. <checkbox
  74. style="transform: scale(0.7)"
  75. :value="itemData.value"
  76. :checked="itemData.checked"
  77. />
  78. <view>{{ itemData.name }}</view>
  79. </label>
  80. </view>
  81. </checkbox-group>
  82. </view>
  83. <!-- 普通选择器 当 range 是一个 Array<Object> 时,通过 range-key 来指定 Object 中 key 的值作为选择器显示内容 :range-key="item[item.value]"-->
  84. <view v-if="item.type === 4" style="width: 100%">
  85. <!-- range[范围] value[初始选择]-->
  86. <picker
  87. :disabled="item.disabled"
  88. :range-key="item.itemKey"
  89. :mode="item.mode ? item.mode : 'selector'"
  90. @change="bindPickerChange($event, item)"
  91. :range="item.itemData"
  92. >
  93. <view class="text">
  94. {{
  95. item[item.value]
  96. ? item.itemKey
  97. ? item[item.value][item.itemKey]
  98. : item[item.value]
  99. : item.hint
  100. ? item.hint
  101. : "请选择" + item.title
  102. }}
  103. </view>
  104. </picker>
  105. </view>
  106. <!-- 富文本框 -->
  107. <view v-if="item.type === 5" style="width: 100%">
  108. <view>
  109. <textarea
  110. :disabled="item.disabled"
  111. class="text"
  112. v-model="item[item.value]"
  113. @input="textareaInput($event, item)"
  114. placeholder-class="text-hint"
  115. style="width: 100%"
  116. :maxlength="item.maxlength"
  117. auto-height
  118. :placeholder="item.hint ? item.hint : '请输入' + item.title"
  119. >
  120. </textarea>
  121. <view
  122. v-if="item.maxlength"
  123. class="as-gravity-center-end"
  124. style="display: flex; flex-direction: row; height: 20%"
  125. >
  126. <view class="text">{{ !item.num ? "0" : item.num }}</view>
  127. <view class="text"
  128. >/{{ item.maxlength ? item.maxlength : 100 }}</view
  129. >
  130. </view>
  131. </view>
  132. </view>
  133. <!-- 图片选择器 :imgWidth="imgWidth ? imgWidth : 0"-->
  134. <view v-if="item.type === 6" style="width: 100%">
  135. <form-image
  136. @backImg="backImg($event, item)"
  137. style="width: 100%"
  138. :retract="40"
  139. ></form-image>
  140. </view>
  141. <!-- 单向选择器 -->
  142. <view v-if="item.type === 7" style="width: 100%">
  143. <radio-group @change="radioChange($event, item)">
  144. <view class="as-layout-horizontal">
  145. <label
  146. style="
  147. display: flex;
  148. flex-direction: row;
  149. margin-right: 30rpx;
  150. "
  151. v-for="(itemData, index) in item.itemData"
  152. :key="index"
  153. >
  154. <radio
  155. style="transform: scale(0.7)"
  156. :value="itemData.value"
  157. :checked="itemData.checked"
  158. />
  159. <view>{{ itemData.name }}</view>
  160. </label>
  161. </view>
  162. </radio-group>
  163. </view>
  164. <!-- 证件照上传 -->
  165. <view v-if="item.type === 8" style="width: 100%">
  166. <view style="margin: 0 40rpx">
  167. <view class="text-error">{{ item.hint }}</view>
  168. <view
  169. class="as-layout-horizontal as-gravity-center"
  170. style="height: 250rpx; margin-top: 20rpx"
  171. >
  172. <view
  173. class="as-layout-vertical"
  174. style="width: 90%; height: 100%"
  175. >
  176. <image
  177. mode="aspectFill"
  178. :src="
  179. item.placeholderImg1
  180. ? item.placeholderImg1
  181. : `${$imgUrl}license2.png`
  182. "
  183. style="width: 100%; height: 80%"
  184. @tap="ImageSelection(item, 1)"
  185. ></image>
  186. <view
  187. class="as-gravity-center text-hint"
  188. style="margin-top: 5rpx"
  189. >
  190. {{ item.hint1 }}
  191. </view>
  192. </view>
  193. <view style="width: 40rpx"></view>
  194. <view
  195. class="as-layout-vertical"
  196. style="width: 90%; height: 100%"
  197. >
  198. <image
  199. :src="
  200. item.placeholderImg2
  201. ? item.placeholderImg2
  202. : `${$imgUrl}license2.png`
  203. "
  204. style="width: 100%; height: 80%"
  205. @tap="ImageSelection(item, 2)"
  206. mode="aspectFill"
  207. ></image>
  208. <view
  209. class="as-gravity-center text-hint"
  210. style="margin-top: 5rpx"
  211. >
  212. {{ item.hint2 }}
  213. </view>
  214. </view>
  215. </view>
  216. </view>
  217. </view>
  218. <!-- 车牌号输入 -->
  219. <view v-if="item.type === 9" style="width: 100%">
  220. <view style="margin: 20rpx 30rpx 0 30rpx">
  221. <car-num-ber-input
  222. @numberInputResult="numberInputResult($event, item)"
  223. >
  224. </car-num-ber-input>
  225. </view>
  226. </view>
  227. <!-- 车牌号输入 -->
  228. <view v-if="item.type === 10" style="width: 100%">
  229. <pick-regions
  230. :defaultRegion="defaultRegionCode"
  231. @getRegion="handleGetRegion($event, item)"
  232. >
  233. <view class="text">
  234. {{
  235. item[item.value]
  236. ? item.itemKey
  237. ? item[item.value][item.itemKey]
  238. : item[item.value]
  239. : item.hint
  240. ? item.hint
  241. : "请选择" + item.title
  242. }}
  243. </view>
  244. </pick-regions>
  245. </view>
  246. </view>
  247. </view>
  248. <view
  249. v-show="index !== formData.length - 1 && !item.underline"
  250. class="as-line3"
  251. style="margin: 0rpx 20rpx 0rpx 20rpx"
  252. ></view>
  253. </view>
  254. </view>
  255. <button class="btn btn-text as-gravity-center" form-type="submit">
  256. {{ config ? config.submitName : "提交" }}
  257. </button>
  258. </form>
  259. </template>
  260. <script setup lang="ts">
  261. import { TypeData } from "./tools";
  262. import { request } from "@/utils/network/request.js";
  263. import carNumBerInput from "@/components/car-number-input/car-number-input";
  264. import { pathToBase64 } from "@/utils/util/imageTool.js";
  265. import pickRegions from "@/components/pick-regions/pick-regions.vue";
  266. import { fileURL } from "@/datas/fileURL.js";
  267. const defaultRegionCode = "520115";
  268. const props = defineProps({
  269. formData: {
  270. type: Array as () => Array<TypeData>,
  271. default: () => [],
  272. },
  273. config: {
  274. type: Object,
  275. default: function () {
  276. return {
  277. submitName: "提交", //提交按钮名称
  278. titleWidth: 160, //标题宽度
  279. };
  280. },
  281. },
  282. });
  283. //defineEmits
  284. const emit = defineEmits<{
  285. (e: "submit", content: any): void;
  286. (e: "uploadImg", content: any, item: TypeData, index: Number): void;
  287. (e: "radioChange", event: any, item: TypeData): void;
  288. }>();
  289. //defineExpose 可宏来显式指定在 <script setup> 组件中要暴露出去的属性。
  290. //普通选择器
  291. function bindPickerChange(e: any, item: TypeData) {
  292. let select = e.target.value;
  293. // #ifdef H5
  294. select = e.detail.value;
  295. // #endif
  296. if (item.mode === "date" || item.mode === "time") {
  297. /* 日期选择器*/
  298. item[item.value] = select;
  299. } else {
  300. /* 普通选择器*/
  301. item[item.value] = item.itemData[select];
  302. }
  303. }
  304. // 获取选择的地区
  305. function handleGetRegion(e: any, item: TypeData) {
  306. console.log("输出内容", e);
  307. item[item.value] = e[0].name + "-" + e[1].name + "-" + e[2].name;
  308. }
  309. //多项选择器
  310. function checkboxChange(e: any, item: TypeData) {
  311. item[item.value] = e.detail.value; /* 赋值*/
  312. }
  313. //单项选择器
  314. function radioChange(e: any, item: TypeData) {
  315. item[item.value] = e.detail.value; /* 赋值*/
  316. emit("radioChange", e, item);
  317. }
  318. //富文本框输入内容
  319. function textareaInput(e: Event, item: TypeData) {
  320. item.num = item[item.value].length;
  321. }
  322. //点击图片按钮
  323. function backImg(e: Event, item: TypeData) {
  324. item[item.value] = e;
  325. }
  326. //车牌输入
  327. function numberInputResult(e: Event, item: TypeData) {
  328. console.log("输出内容", e);
  329. item[item.value] = e;
  330. }
  331. //选择图片按钮
  332. function ImageSelection(item: TypeData, index: number) {
  333. uni.chooseImage({
  334. count: 1, //最多可以选择的文件个数
  335. sourceType: ["camera"], //album 从相册选视频,camera 使用相机拍摄,默认为:['album', 'camera']
  336. success(res: any) {
  337. if (res.tempFiles[0].size > 2000000) {
  338. uni.showToast({
  339. title: "图片大于2M,请重新上传",
  340. icon: "none",
  341. duration: 1500,
  342. });
  343. return;
  344. }
  345. // #ifdef MP-WEIXIN
  346. uni.getFileSystemManager().readFile({
  347. filePath: res.tempFiles[0].tempFilePath, //要读取的文件的路径
  348. encoding: "base64", //编码格式
  349. success: (res) => {
  350. let imgBase64 = res.data;
  351. let reqData = {
  352. imageBase: imgBase64,
  353. accountNum: "qtzl_xcx",
  354. secretKey: "4DE47302-EDC7-41D4-888F-59A79DF4EA46",
  355. };
  356. //上传给服务器
  357. uni.showLoading({
  358. title: "正在加载中...",
  359. mask: true,
  360. });
  361. //文件上传
  362. request("", {
  363. baseUrl: "https://etcfile.etcjz.cn/v1/file/uploadImageBaseFile",
  364. data: reqData,
  365. }).then((content) => {
  366. uni.hideLoading();
  367. if (content.rc != "00") {
  368. uni.showModal({
  369. title: "请求错误",
  370. content: "图片上传失败",
  371. showCancel: false,
  372. confirmText: "取消",
  373. });
  374. }
  375. //处理后的 Data
  376. let data = {
  377. source: 2, //来源
  378. imageBase64: imgBase64,
  379. imageType: index,
  380. imageUrl: content.data.fileUrl,
  381. };
  382. emit("uploadImg", data, item, index);
  383. });
  384. },
  385. fail: (err) => {
  386. console.log("错误提示", err);
  387. },
  388. });
  389. // #endif
  390. // #ifdef H5
  391. pathToBase64(res.tempFilePaths[0]).then((data) => {
  392. let base64 = data.replaceAll("data:image/jpeg;base64,", "");
  393. base64 = base64.replaceAll("data:image/png;base64,", "");
  394. base64 = base64.replaceAll("data:image/jpg;base64,", "");
  395. let reqData = {
  396. imageBase: base64,
  397. accountNum: "qtzl_xcx",
  398. secretKey: "4DE47302-EDC7-41D4-888F-59A79DF4EA46",
  399. };
  400. //上传给服务器
  401. uni.showLoading({
  402. title: "文件上传中...",
  403. mask: true,
  404. });
  405. //文件上传
  406. request("", {
  407. baseUrl: "https://etcfile.etcjz.cn/v1/file/uploadImageBaseFile",
  408. data: reqData,
  409. })
  410. .then((content) => {
  411. console.log("上传结果", content);
  412. uni.hideLoading();
  413. if (content.rc != "00" && content.rc != 0) {
  414. uni.showModal({
  415. title: "请求错误",
  416. content: "图片上传失败",
  417. showCancel: false,
  418. confirmText: "取消",
  419. });
  420. }
  421. //展示缩略图
  422. if (index % 2 != 0) {
  423. //奇数第一张 偶数第二张
  424. item.placeholderImg1 = content.data.fileUrl;
  425. item[item.value.split(",")[0]] = content.data.fileUrl;
  426. } else {
  427. item.placeholderImg2 = content.data.fileUrl;
  428. item[item.value.split(",")[1]] = content.data.fileUrl;
  429. }
  430. //只展示缩略图
  431. if (!item.inputType) {
  432. emit("uploadImg", data, item, index);
  433. } else {
  434. //if(item.type === 1)
  435. //证件OCR识别
  436. uni.showLoading({
  437. title: "证件识别中...",
  438. mask: true,
  439. });
  440. let reqData2 = {
  441. imageDriveBase: "",
  442. imageBase64: base64,
  443. imageType: index % 2 != 0 ? 1 : 2, //1代表身份证正面,2代表反面
  444. imageUrl: content.data.fileUrl,
  445. };
  446. //默认身份证
  447. let code = "IF010012019070410001";
  448. if (item.inputType == "2") {
  449. //2为行驶证
  450. code = "IF010012019070410002";
  451. reqData2.imageBase64 = "";
  452. reqData2.imageDriveBase = base64;
  453. }
  454. request(code, {
  455. data: reqData2,
  456. }).then((ocr) => {
  457. uni.hideLoading();
  458. if (ocr.rc !== "00") {
  459. uni.showModal({
  460. title: "请求错误",
  461. content: "证件识别失败",
  462. showCancel: false,
  463. confirmText: "取消",
  464. });
  465. }
  466. let data = ocr.rd;
  467. //[身份证正面]姓名(name) 住址(address) 出生日期(birthday) 身份证号(idno) 民族(nation)
  468. //[身份证反面]证件机关(agency) 有效起始日期(begindate) 有效结束日期(enddate)
  469. emit("uploadImg", data, item, index);
  470. });
  471. }
  472. })
  473. .catch((e) => {
  474. console.log("输出内容", e);
  475. });
  476. });
  477. // #endif
  478. },
  479. fail(res: any) {
  480. if (!res.authSetting["scope.album"]) {
  481. uni.showModal({
  482. title: "授权失败",
  483. content: "需要从您的相机或相册获取图片,请在设置界面打开相关权限",
  484. success: (res) => {
  485. if (res.confirm) {
  486. uni.openSetting();
  487. }
  488. },
  489. });
  490. }
  491. },
  492. });
  493. }
  494. function showToast(hint: string) {
  495. uni.showToast({
  496. icon: "none",
  497. title: hint,
  498. });
  499. }
  500. //内容提交
  501. function formSubmit() {
  502. let content = {};
  503. /* 整理数据对象返回内容 */
  504. for (var i = 0; i < props.formData.length; i++) {
  505. let data = props.formData[i];
  506. /* 时间另外判断 */
  507. if (data.required) {
  508. let reg = new RegExp(":", "g"); //g代表全部
  509. let newMsg = data.title.replace(reg, "");
  510. if (data.value.indexOf(",") != -1 && data.type === 8) {
  511. if (!data[data.value.split(",")[0]]) {
  512. showToast(data.hint1 + "不能为空");
  513. return;
  514. } else if (!data[data.value.split(",")[1]]) {
  515. showToast(data.hint2 + "不能为空");
  516. return;
  517. }
  518. } else if (!data[data.value]) {
  519. showToast(newMsg + "不能为空");
  520. return;
  521. }
  522. }
  523. if (data.value.indexOf(",") != -1 && data.type === 8) {
  524. if (data[data.value.split(",")[0]]) {
  525. content[data.value.split(",")[0]] = data[data.value.split(",")[0]];
  526. }
  527. if (data[data.value.split(",")[1]]) {
  528. content[data.value.split(",")[1]] = data[data.value.split(",")[1]];
  529. }
  530. } else if (data.type === 10) {
  531. console.log("输出内容========", data);
  532. for (var i = 0; i < data.value.split(",").length; i++) {
  533. content[data.value.split(",")[i]] = data[data.value].split("-")[i];
  534. }
  535. } else {
  536. if (data.type === 4 && data.name) {
  537. content[data.value] = data[data.value][data.name];
  538. } else {
  539. content[data.value] = data[data.value];
  540. }
  541. }
  542. }
  543. emit("submit", content);
  544. }
  545. </script>
  546. <style lang="scss" scoped>
  547. .img-size {
  548. width: 30rpx;
  549. height: 30rpx;
  550. }
  551. .btn {
  552. height: 80rpx;
  553. background: #307afe;
  554. opacity: 1;
  555. border-radius: 100rpx;
  556. margin: 20rpx 20rpx 20rpx 20rpx;
  557. }
  558. .btn-text {
  559. color: #ffffff;
  560. font-size: 28rpx;
  561. }
  562. </style>