pcSubmit.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <template>
  2. <div class="pcSubmit">
  3. <a-form layout="vertical" :model="formData" ref="formRef" :rules="rules">
  4. <a-form-item name="problemDesc">
  5. <div class="myTitle required" slot="label"><span class="number">01</span>{{ t('feedback.title1') }}</div>
  6. <a-textarea
  7. show-count
  8. :maxlength="500"
  9. :autoSize="{ minRows: 4, maxRows: 8 }"
  10. v-model:value="formData.problemDesc"
  11. :placeholder="t('feedback.settext')"
  12. />
  13. </a-form-item>
  14. <a-form-item name="problemFile" style="margin-top: 2px">
  15. <a-upload
  16. v-model:file-list="formData.problemDescImgs"
  17. accept=".jpg,.png,.mp4"
  18. action="/service/manage/common/upload/files"
  19. list-type="picture-card"
  20. :maxCount="6"
  21. :multiple="true"
  22. :before-upload="beforeUpload"
  23. @preview="handlePreview"
  24. @change="fileChange"
  25. >
  26. <div>
  27. <PlusOutlined style="color: #d9d9d9" color="#D9D9D9" />
  28. <!-- <div style="margin-top: 8px">{{ t('feedback.upload') }}</div> -->
  29. </div>
  30. </a-upload>
  31. <div class="tips" v-html="t('feedback.fileTipsPc')"></div>
  32. </a-form-item>
  33. <a-form-item name="solution">
  34. <div class="myTitle required" slot="label"><span class="number">02</span>{{ t('feedback.title2') }}</div>
  35. <a-textarea
  36. show-count
  37. :maxlength="500"
  38. :autoSize="{ minRows: 4, maxRows: 8 }"
  39. v-model:value="formData.solution"
  40. :placeholder="t('feedback.settext')"
  41. />
  42. </a-form-item>
  43. <a-form-item name="problemFile" style="margin-top: 2px">
  44. <a-upload
  45. :maxCount="6"
  46. v-model:file-list="formData.solutionImgs"
  47. action="/service/manage/common/upload/files"
  48. list-type="picture-card"
  49. :multiple="true"
  50. :before-upload="beforeUpload"
  51. @preview="handlePreview"
  52. @change="fileChange"
  53. >
  54. <div>
  55. <PlusOutlined style="color: #d9d9d9" color="#D9D9D9" />
  56. <!-- <div style="margin-top: 8px">{{ t('feedback.upload') }}</div> -->
  57. </div>
  58. </a-upload>
  59. <div class="tips" v-html="t('feedback.fileTipsPc')"></div>
  60. </a-form-item>
  61. <a-form-item name="industryOptionId">
  62. <div class="myTitle required" slot="label"><span class="number">03</span>{{ t('feedback.title3') }}</div>
  63. <a-select
  64. key="industryOptionId"
  65. v-model:value="formData.industryOptionId"
  66. :placeholder="t('feedback.setselcet')"
  67. :options="propsOptions.industryOptionId"
  68. />
  69. </a-form-item>
  70. <a-form-item name="hardwareOptionId">
  71. <div class="myTitle required" slot="label"><span class="number">04</span>{{ t('feedback.title4') }}</div>
  72. <a-select
  73. key="hardwareOptionId"
  74. v-model:value="formData.hardwareOptionId"
  75. :placeholder="t('feedback.setselcet')"
  76. :options="propsOptions.hardwareOptionId"
  77. />
  78. </a-form-item>
  79. <a-form-item name="softwareOptionId">
  80. <div class="myTitle required" slot="label"><span class="number">05</span>{{ t('feedback.title5') }}</div>
  81. <a-select
  82. key="softwareOptionId"
  83. v-model:value="formData.softwareOptionId"
  84. :placeholder="t('feedback.setselcet')"
  85. :options="propsOptions.softwareOptionId"
  86. />
  87. </a-form-item>
  88. <a-form-item>
  89. <div class="myTitle" slot="label"><span class="number">06</span>{{ t('feedback.title6') }}</div>
  90. <a-input :maxlength="30" v-model:value="formData.nickName" :placeholder="t('feedback.settext')" />
  91. </a-form-item>
  92. <a-form-item>
  93. <div class="myTitle" slot="label"><span class="number">07</span>{{ t('feedback.title61') }}</div>
  94. <a-input :maxlength="30" v-model:value="formData.phone" :placeholder="t('feedback.settext')" />
  95. </a-form-item>
  96. <a-form-item>
  97. <div class="myTitle" slot="label"><span class="number">08</span>{{ t('feedback.title7') }}</div>
  98. <a-select
  99. v-model:value="formData.country"
  100. :filterOption="filterOption"
  101. :options="countryOption"
  102. showSearch
  103. :placeholder="t('feedback.setselcet')"
  104. />
  105. <!-- <a-cascader v-model:value="formData.countries" :options="options" placeholder="Please select" /> -->
  106. </a-form-item>
  107. <a-form-item v-if="formData.country == '中国' || formData.country == 'China'">
  108. <div class="myTitle" slot="label"
  109. ><span class="number">{{ formData.country == '中国' || formData.country == 'China' ? '09' : '08' }}</span
  110. >{{ t('feedback.title71') }}</div
  111. >
  112. <a-cascader v-model:value="formData.countries" :options="options" :placeholder="t('feedback.setselcet')" />
  113. </a-form-item>
  114. <a-form-item>
  115. <div class="myTitle" slot="label"
  116. ><span class="number">{{ formData.country == '中国' || formData.country == 'China' ? '10' : '09' }}</span
  117. >{{ t('feedback.title8') }}</div
  118. >
  119. <van-rate
  120. color="#FADB14"
  121. size="30"
  122. void-color="#D9D9D9"
  123. :allow-half="true"
  124. v-model="formData.score"
  125. :placeholder="t('feedback.setselcet')"
  126. />
  127. </a-form-item>
  128. <a-form-item>
  129. <div class="myTitle" slot="label"
  130. ><span class="number">{{ formData.country == '中国' || formData.country == 'China' ? '11' : '10' }}</span
  131. >{{ t('feedback.title9') }}</div
  132. >
  133. <a-input :maxlength="100" v-model:value="formData.scoreReason" :placeholder="t('feedback.settext')" />
  134. </a-form-item>
  135. <a-form-item style="text-align: center">
  136. <a-button class="w-160px" style="background-color: #00b3ec" type="primary" size="large" @click="onSubmit">{{
  137. t('feedback.Submit')
  138. }}</a-button>
  139. </a-form-item>
  140. </a-form>
  141. </div>
  142. </template>
  143. <script setup>
  144. import { ref, watch } from 'vue';
  145. import { PlusOutlined } from '@ant-design/icons-vue';
  146. import cityList from './area.json';
  147. import countryList from './country.json';
  148. // import { createImgPreview } from '/@/components/Preview/index';
  149. const props = defineProps(['formData', 'columns', 'addres']);
  150. import { message, Upload } from 'ant-design-vue';
  151. import { useI18n } from 'vue-i18n';
  152. const { t, locale } = useI18n();
  153. const emit = defineEmits(['submit']);
  154. watch(
  155. () => props.addres,
  156. (newValue, oldValue) => {
  157. console.log('addreswatch', newValue);
  158. formData.value.country = newValue.country;
  159. formData.value.countries = newValue.mccountries;
  160. },
  161. );
  162. const formData = ref({
  163. problemDesc: '',
  164. problemDescImgs: [],
  165. hardwareOptionId: null,
  166. softwareOptionId: null,
  167. industryOptionId: null,
  168. solution: '',
  169. solutionImgs: [],
  170. nickName: '',
  171. phone: '',
  172. score: 0,
  173. scoreReason: '',
  174. country: '',
  175. address: '',
  176. countries: [],
  177. });
  178. console.log('formData', formData);
  179. // eslint-disable-next-line vue/no-setup-props-destructure
  180. const propsOptions = props.columns;
  181. const previewImage = ref('');
  182. const formRef = ref(null);
  183. const rules = {
  184. problemDesc: [
  185. { name: 'problemDesc', validator: validatorFile, message: t('feedback.settext') + t('feedback.title1'), trigger: 'change' },
  186. ],
  187. solution: [{ name: 'solution', validator: validatorFile, message: t('feedback.settext') + t('feedback.title2'), trigger: 'change' }],
  188. softwareOptionId: [{ required: true, message: t('feedback.setselcet') + t('feedback.title5'), trigger: 'change' }],
  189. industryOptionId: [{ required: true, message: t('feedback.setselcet') + t('feedback.title3'), trigger: 'change' }],
  190. hardwareOptionId: [{ required: true, message: t('feedback.setselcet') + t('feedback.title4'), trigger: 'change' }],
  191. };
  192. function validatorFile(_rule, value) {
  193. let rule = _rule;
  194. let isOk = Boolean(formData.value[rule.name] || (formData.value[`${rule.name}Imgs`] && formData.value[`${rule.name}Imgs`].length > 0));
  195. if (isOk) {
  196. return Promise.resolve();
  197. } else {
  198. return Promise.reject(rule.message);
  199. }
  200. }
  201. function fileChange() {
  202. formRef.value.validateFields(['problemDesc', 'solution']);
  203. }
  204. const countryOption = countryList.map((ele) => {
  205. return {
  206. value: ele.chinese,
  207. label: locale.value == 'en-us' ? ele.english : ele.chinese,
  208. english: locale.value == 'en-us' ? ele.chinese : ele.english,
  209. };
  210. });
  211. function filterOption(inputValue, option) {
  212. return (
  213. option.label.toLowerCase().indexOf(inputValue.toLowerCase()) >= 0 ||
  214. option.english.toLowerCase().indexOf(inputValue.toLowerCase()) >= 0
  215. );
  216. }
  217. const options = cityList.map((ele) => {
  218. return {
  219. value: ele.name,
  220. label: ele.name,
  221. children: ele.city.map((element) => {
  222. return {
  223. value: element.name,
  224. label: element.name,
  225. };
  226. }),
  227. };
  228. });
  229. const beforeUpload = (file) => {
  230. console.log('beforeUpload', file);
  231. const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'video/mp4';
  232. if (!isJpgOrPng) {
  233. message.error(t('feedback.fileTips'));
  234. return false;
  235. }
  236. const isLt2M = file.size / 1024 / 1024 < 5;
  237. const isLt50M = file.size / 1024 / 1024 < 50;
  238. if (!isLt2M && (file.type === 'image/jpeg' || file.type === 'image/png')) {
  239. message.error(t('feedback.fileTips'));
  240. return false;
  241. }
  242. if (!isLt50M && file.type === 'video/mp4') {
  243. message.error(t('feedback.fileTips'));
  244. return Upload.LIST_IGNORE;
  245. }
  246. return file.type === 'video/mp4' ? isLt50M && isJpgOrPng : isJpgOrPng && isLt2M;
  247. };
  248. // const previewFile = (file) => {
  249. // console.log('file',file)
  250. // return file
  251. // }
  252. function getBase64(file) {
  253. return new Promise((resolve, reject) => {
  254. const reader = new FileReader();
  255. reader.readAsDataURL(file);
  256. reader.onload = () => resolve(reader.result);
  257. reader.onerror = (error) => reject(error);
  258. });
  259. }
  260. const handlePreview = async (file) => {
  261. console.log('file', file);
  262. file.preview = file.response.data;
  263. window.open(file.response.data);
  264. return;
  265. // createImgPreview({ imageList: [file.response.data] });
  266. };
  267. const preview = (file) => {
  268. console.log('file', file);
  269. return file;
  270. };
  271. const onSubmit = () => {
  272. console.log('values', formRef.value.validate());
  273. formRef.value
  274. .validate()
  275. .then(() => {
  276. emit('submit', formData.value);
  277. console.log('values', formData);
  278. })
  279. .catch((error) => {
  280. console.log('error', error);
  281. });
  282. };
  283. </script>
  284. <style lang="scss" scoped>
  285. .myTitle {
  286. position: relative;
  287. margin-bottom: 14px;
  288. margin-top: 48px;
  289. .number {
  290. font-size: 16px;
  291. font-family: Microsoft YaHei, Microsoft YaHei;
  292. font-weight: bold;
  293. color: #00b3ec;
  294. }
  295. span {
  296. font-size: 16px;
  297. font-family: Microsoft YaHei, Microsoft YaHei;
  298. font-weight: 400;
  299. color: #333333;
  300. line-height: 19px;
  301. margin-right: 7px;
  302. }
  303. }
  304. .tips {
  305. font-size: 14px;
  306. font-family: Microsoft YaHei, Microsoft YaHei;
  307. font-weight: 400;
  308. color: #cccccc;
  309. line-height: 16px;
  310. margin-top: 8px;
  311. }
  312. .required {
  313. &::before {
  314. display: inline-block;
  315. margin-inline-end: 4px;
  316. color: #ff4d4f;
  317. font-size: 14px;
  318. font-family: SimSun, sans-serif;
  319. line-height: 1;
  320. content: '*';
  321. position: absolute;
  322. left: -11px;
  323. top: 4.5px;
  324. }
  325. }
  326. </style>