mcSubmit.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. <template>
  2. <div class="mcSubmit">
  3. <van-form @submit="onSubmit" ref="submits" style="background: #fff">
  4. <van-cell-group style="margin: none" inset>
  5. <div class="myTitle required"><span class="number">01</span>{{ t('feedback.title1') }}</div>
  6. <van-field
  7. class="zdycell"
  8. v-model="formData.problemDesc"
  9. label-align="top"
  10. name="故障描述"
  11. label=""
  12. rows="2"
  13. autoSize
  14. :max-count="6"
  15. type="textarea"
  16. maxlength="500"
  17. show-word-limit
  18. :rules="[
  19. { name: 'problemDesc', validator: validator, trigger: 'onChange', message: t('feedback.settext') + t('feedback.title1') },
  20. ]"
  21. :placeholder="t('feedback.settext')"
  22. />
  23. <van-field class="uploadercell" name="uploader" label-align="top">
  24. <template #input>
  25. <div>
  26. <div>
  27. <van-uploader
  28. upload-icon="plus"
  29. :multiple="true"
  30. :before-read="beforeRead"
  31. :after-read="clzpAfterRead"
  32. accept=".jpg,.png,.mp4"
  33. :max-count="6"
  34. v-model="formData.problemDescImgs"
  35. />
  36. </div>
  37. <div class="tips" v-html="t('feedback.fileTipsPc')"></div>
  38. </div>
  39. </template>
  40. </van-field>
  41. <div class="myTitle required"><span class="number">02</span>{{ t('feedback.title2') }}</div>
  42. <van-field
  43. v-model="formData.solution"
  44. label-align="top"
  45. class="zdycell"
  46. name="故障描述"
  47. label=""
  48. rows="2"
  49. autoSize
  50. type="textarea"
  51. maxlength="500"
  52. show-word-limit
  53. :rules="[{ name: 'solution', validator: validator, trigger: 'onChange', message: t('feedback.settext') + t('feedback.title2') }]"
  54. :placeholder="t('feedback.settext')"
  55. />
  56. <van-field class="uploadercell" name="uploader" label-align="top">
  57. <template #input>
  58. <div>
  59. <div>
  60. <van-uploader
  61. upload-icon="plus"
  62. :multiple="true"
  63. class="uploadercell"
  64. :before-read="beforeRead"
  65. :after-read="clzpAfterRead"
  66. :max-count="6"
  67. accept=".jpg,.png,.mp4"
  68. v-model="formData.solutionImgs"
  69. />
  70. </div>
  71. <div class="tips" v-html="t('feedback.fileTipsPc')"></div>
  72. </div>
  73. </template>
  74. </van-field>
  75. <div class="myTitle required"><span class="number">03</span>{{ t('feedback.title3') }}</div>
  76. <van-field
  77. v-model="setObjId.industryOptionId"
  78. class="zdycell"
  79. is-link
  80. readonly
  81. name="picker"
  82. label=""
  83. :placeholder="t('feedback.setselcet')"
  84. :rules="[{ required: true, message: t('feedback.setselcet') + t('feedback.title3') }]"
  85. @click="showPicker.industryOptionId = true"
  86. />
  87. <van-popup v-model:show="showPicker.industryOptionId" position="bottom">
  88. <van-picker
  89. :columns="columns.industryOptionId"
  90. @confirm="(val) => onConfirm(val, 'industryOptionId')"
  91. @cancel="showPicker.industryOptionId = false"
  92. />
  93. </van-popup>
  94. <div class="myTitle required"><span class="number">04</span>{{ t('feedback.title4') }}</div>
  95. <van-field
  96. class="zdycell"
  97. v-model="setObjId.hardwareOptionId"
  98. is-link
  99. readonly
  100. name="picker"
  101. label=""
  102. :placeholder="t('feedback.setselcet')"
  103. :rules="[{ required: true, message: t('feedback.setselcet') + t('feedback.title4') }]"
  104. @click="showPicker.hardwareOptionId = true"
  105. />
  106. <van-popup v-model:show="showPicker.hardwareOptionId" position="bottom">
  107. <van-picker
  108. :columns="columns.hardwareOptionId"
  109. @confirm="(val) => onConfirm(val, 'hardwareOptionId')"
  110. @cancel="showPicker.hardwareOptionId = false"
  111. />
  112. </van-popup>
  113. <div class="myTitle required"><span class="number">05</span>{{ t('feedback.title5') }}</div>
  114. <van-field
  115. class="zdycell"
  116. v-model="setObjId.softwareOptionId"
  117. is-link
  118. readonly
  119. name="picker"
  120. label=""
  121. maxlength="30"
  122. :placeholder="t('feedback.setselcet')"
  123. :rules="[{ required: true, message: t('feedback.setselcet') + t('feedback.title5') }]"
  124. @click="showPicker.softwareOptionId = true"
  125. />
  126. <van-popup v-model:show="showPicker.softwareOptionId" position="bottom">
  127. <van-picker
  128. :columns="columns.softwareOptionId"
  129. @confirm="(val) => onConfirm(val, 'softwareOptionId')"
  130. @cancel="showPicker.softwareOptionId = false"
  131. />
  132. </van-popup>
  133. <div class="myTitle"><span class="number">06</span>{{ t('feedback.title6') }}</div>
  134. <van-field
  135. class="zdycell"
  136. v-model="formData.nickName"
  137. label-align="top"
  138. name="联系电话"
  139. maxlength="15"
  140. label=""
  141. :placeholder="t('feedback.settext')"
  142. />
  143. <div class="myTitle"><span class="number">07</span>{{ t('feedback.title61') }}</div>
  144. <van-field
  145. class="zdycell"
  146. v-model="formData.phone"
  147. label-align="top"
  148. name="联系电话"
  149. maxlength="11"
  150. label=""
  151. :placeholder="t('feedback.settext')"
  152. />
  153. <div class="myTitle"><span class="number">08</span>{{ t('feedback.title7') }}</div>
  154. <van-field
  155. v-model="formData.country"
  156. is-link
  157. class="zdycell"
  158. readonly
  159. name="picker"
  160. label=""
  161. :placeholder="t('feedback.setselcet')"
  162. @click="showPicker.country = true"
  163. />
  164. <van-popup v-model:show="showPicker.country" position="bottom">
  165. <van-picker :columns="columnsCountry" @confirm="(val) => onConfirm(val, 'country')" @cancel="showPicker.country = false" />
  166. </van-popup>
  167. <div class="myTitle" v-if="formData.country == '中国' || formData.country == 'China'"
  168. ><span class="number">{{ formData.country == '中国' || formData.country == 'China' ? '09' : '08' }}</span
  169. >{{ t('feedback.title71') }}</div
  170. >
  171. <van-field
  172. v-if="formData.country == '中国' || formData.country == 'China'"
  173. v-model="formData.city"
  174. class="zdycell"
  175. is-link
  176. readonly
  177. name="area"
  178. label=""
  179. :placeholder="t('feedback.setselcet')"
  180. @click="showPicker.city = true"
  181. />
  182. <van-popup v-model:show="showPicker.city" position="bottom">
  183. <van-cascader
  184. v-model="cascaderValue"
  185. title="请选择所在地区"
  186. :options="columnsCity"
  187. @close="showPicker.city = false"
  188. @finish="onFinish"
  189. />
  190. <!-- ?x-oss-process=video/snapshot,t_0,f_jpg,w_1000,m_fast,ar_auto -->
  191. </van-popup>
  192. <div class="myTitle"
  193. ><span class="number">{{ formData.country == '中国' || formData.country == 'China' ? '10' : '09' }}</span
  194. >{{ t('feedback.title8') }}</div
  195. >
  196. <van-field class="ratecell" name="rate" label="">
  197. <template #input>
  198. <van-rate size="30" color="#FADB14" void-color="#D9D9D9" :allow-half="false" v-model="formData.score" />
  199. </template>
  200. </van-field>
  201. <div class="myTitle"
  202. ><span class="number">{{ formData.country == '中国' || formData.country == 'China' ? '11' : '10' }}</span
  203. >{{ t('feedback.title9') }}</div
  204. >
  205. <van-field
  206. v-model="formData.scoreReason"
  207. class="zdycell"
  208. label-align="top"
  209. name="联系电话"
  210. maxlength="100"
  211. label=""
  212. :placeholder="t('feedback.settext')"
  213. />
  214. <div style="margin: 54px 0 24px 0; background-color: #f5f5f5">
  215. <van-button style="height: 44px" block color="#00B3EC" type="primary" native-type="submit"> 提交 </van-button>
  216. </div>
  217. </van-cell-group>
  218. </van-form>
  219. </div>
  220. </template>
  221. <script setup>
  222. import { ref, watch } from 'vue';
  223. import cityList from './area.json';
  224. import countryList from './country.json';
  225. import axios from 'axios';
  226. // const areaList = ref({});
  227. const props = defineProps(['columns', 'addres']);
  228. const emit = defineEmits(['submit']);
  229. import { showToast } from 'vant';
  230. // const columns = props.columns;
  231. const loading = ref(false);
  232. const submits = ref(null);
  233. const setObjId = ref({
  234. hardwareOptionId: null,
  235. softwareOptionId: null,
  236. industryOptionId: null,
  237. });
  238. const cascaderValue = ref();
  239. watch(
  240. () => props.addres,
  241. (newValue, _) => {
  242. console.log('addreswatch', newValue);
  243. formData.value.country = newValue.country;
  244. formData.value.city = newValue.mccountries;
  245. cascaderValue.value = newValue.city;
  246. // 因为watch被观察的对象只能是getter/effect函数、ref、active对象或者这些类型是数组
  247. // 所以需要将state.count变成getter函数
  248. },
  249. );
  250. const formData = ref({
  251. problemDesc: '',
  252. problemDescImgs: [],
  253. hardwareOptionId: null,
  254. softwareOptionId: null,
  255. industryOptionId: null,
  256. solution: '',
  257. solutionImgs: [],
  258. nickName: '',
  259. phone: '',
  260. address: '',
  261. score: 0,
  262. scoreReason: '',
  263. });
  264. import { useI18n } from 'vue-i18n';
  265. const { t, locale } = useI18n();
  266. const showPicker = ref({
  267. country: false,
  268. hardwareOptionId: false,
  269. softwareOptionId: false,
  270. city: false,
  271. });
  272. const columnsCountry = countryList.map((ele) => {
  273. return { text: locale.value == 'en-us' ? ele.english : ele.chinese, value: locale.value == 'en-us' ? ele.english : ele.chinese };
  274. });
  275. const columnsCity = cityList.map((ele) => {
  276. return {
  277. text: ele.name,
  278. value: ele.name,
  279. children: ele.city.map((element) => {
  280. return {
  281. text: element.name,
  282. value: element.name,
  283. };
  284. }),
  285. };
  286. });
  287. // 全部选项选择完毕后,会触发 finish 事件
  288. const onFinish = ({ selectedOptions }, _) => {
  289. showPicker.value.city = false;
  290. console.log('onFinish', cascaderValue.value);
  291. formData.value.city = selectedOptions.map((option) => option.text).join('/');
  292. };
  293. const onConfirm = ({ selectedOptions, selectedValues }, b) => {
  294. formData.value[b] = selectedValues.join(',');
  295. setObjId.value[b] = selectedOptions[0].text;
  296. showPicker.value[b] = false;
  297. };
  298. const onSubmit = () => {
  299. emit('submit', formData.value, setObjId.value);
  300. };
  301. // const beforeUpload = (file) => {
  302. // console.log('beforeUpload', file);
  303. // const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
  304. // if (!isJpgOrPng) {
  305. // showToast('You can only upload JPG file!');
  306. // }
  307. // const isLt2M = file.size / 1024 / 1024 < 2;
  308. // if (!isLt2M) {
  309. // showToast('Image must smaller than 2MB!');
  310. // }
  311. // return isJpgOrPng && isLt2M;
  312. // }; //校验图片的格式
  313. function beforeRead(file) {
  314. const isJpgOrPng =
  315. file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'video/mp4' || file.type === 'video/quicktime';
  316. if (!isJpgOrPng) {
  317. showToast(t('feedback.fileTips'));
  318. return false;
  319. }
  320. const isLt2M = file.size / 1024 / 1024 < 5;
  321. const isLt50M = file.size / 1024 / 1024 < 50;
  322. if (!isLt2M && (file.type === 'image/jpeg' || file.type === 'image/png')) {
  323. showToast(t('feedback.fileTips'));
  324. return false;
  325. }
  326. if (!isLt50M && (file.type === 'video/mp4' || file.type === 'video/quicktime')) {
  327. showToast(t('feedback.fileTips'));
  328. return false;
  329. }
  330. // if (!/(jpg|jpeg|png|JPG|PNG|mp4|quicktime)/i.test(file.type)) {
  331. // showToast(t('feedback.fileTips'));
  332. // return false;
  333. // }
  334. return true;
  335. }
  336. function validator(value, rule) {
  337. let isOk = Boolean(formData.value[rule.name] || (formData.value[`${rule.name}Imgs`] && formData.value[`${rule.name}Imgs`].length > 0));
  338. return isOk;
  339. }
  340. //照片上传事件方法
  341. function clzpAfterRead(file) {
  342. // 上传状态提示开启
  343. file.status = 'uploading';
  344. file.message = '上传中...';
  345. loading.value = true;
  346. // 创建一个空对象实例
  347. let formData = new FormData();
  348. // 调用append()方法添加数据
  349. formData.append('file', file.file);
  350. axios({
  351. url: '/service/manage/common/upload/files',
  352. method: 'POST',
  353. data: formData,
  354. headers: {
  355. 'Content-Type': 'multipart/form-data',
  356. },
  357. })
  358. .then((res) => {
  359. loading.value = false;
  360. let { data } = res;
  361. if (data.code == 200 || data.code == 0) {
  362. // 上传状态提示关闭
  363. file.url = data.data;
  364. file.file = file.file;
  365. file.content = '';
  366. file.status = 'done';
  367. showToast('上传成功!');
  368. setTimeout(() => {
  369. console.log('problemDesc', submits.value);
  370. // submits.value.validate('problemDesc')();
  371. // submits.value.validate('solution');
  372. }, 1000);
  373. }
  374. console.log('formData.faultImg', formData, file);
  375. })
  376. .catch(() => {
  377. loading.value = false;
  378. });
  379. }
  380. </script>
  381. <style lang="scss" scoped>
  382. .mcSubmit {
  383. padding-top: 16px;
  384. .tips {
  385. font-size: 14px;
  386. font-family: Source Han Sans CN, Source Han Sans CN;
  387. font-weight: 400;
  388. color: #cccccc;
  389. line-height: 16px;
  390. margin-top: 15px;
  391. }
  392. .required {
  393. &::before {
  394. display: inline-block;
  395. margin-inline-end: 4px;
  396. color: #ff4d4f;
  397. font-size: 14px;
  398. font-family: SimSun, sans-serif;
  399. line-height: 1;
  400. content: '*';
  401. position: absolute;
  402. left: -11px;
  403. top: 4.5px;
  404. }
  405. }
  406. .myTitle {
  407. position: relative;
  408. margin: 48px 0 15px 0;
  409. font-size: 16px;
  410. // padding: 0 var(--van-cell-horizontal-padding);
  411. .number {
  412. font-size: 16px;
  413. font-family: Microsoft YaHei, Microsoft YaHei;
  414. font-weight: bold;
  415. color: #00b3ec;
  416. }
  417. span {
  418. font-size: 16px;
  419. font-family: Microsoft YaHei, Microsoft YaHei;
  420. font-weight: 400;
  421. color: #333333;
  422. line-height: 19px;
  423. margin-right: 7px;
  424. }
  425. }
  426. ::v-deep .uploadercell:after {
  427. display: none;
  428. }
  429. ::v-deep .ratecell:after {
  430. display: none;
  431. }
  432. ::v-deep .van-cell {
  433. padding: 4px 0px;
  434. }
  435. // ::v-deep .van-uploader__upload{
  436. // background-color: #fff;
  437. // border: 1px solid #e4e4e5;
  438. // border-radius: 5px;
  439. // }
  440. // .zdycell {
  441. // border: 1px solid #e4e4e5;
  442. // border-radius: 5px;
  443. // }
  444. .uploadercell {
  445. padding: 20px 0 0 0;
  446. }
  447. }
  448. </style>