suggestion-option.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. import { defineComponent, onMounted, reactive, ref } from 'vue';
  2. import styles from './suggestion-option.module.less';
  3. import {
  4. NButton,
  5. NForm,
  6. NFormItem,
  7. NInput,
  8. useMessage,
  9. NUpload,
  10. UploadFileInfo,
  11. NImage,
  12. UploadCustomRequestOptions,
  13. NModal,
  14. NBadge
  15. } from 'naive-ui';
  16. import { useUserStore } from '/src/store/modules/users';
  17. import bgLine from '../images/bg-line.png';
  18. import chioseAdd from '../images/chioseAdd.png';
  19. import CSelect from '../../CSelect';
  20. import suggestClose from '../images/suggestClose.png';
  21. import inFront from '../images/inFront.png';
  22. import inBack from '../images/inBack.png';
  23. import submitBtn from '../images/submitBtn.png';
  24. import sealing from '../images/sealing.png';
  25. import boxBg from '../images/boxBg.png';
  26. import {
  27. addSuggestion,
  28. getSuggestionList,
  29. sysParamConfigPage
  30. } from '../modals/api';
  31. import { getUploadSign, onFileUpload } from '/src/helpers/oss-file-upload';
  32. import SuggestionList from './suggestion-list';
  33. import { suggestMessageUnread } from '/src/api/user';
  34. import { modalClickMask } from '/src/state';
  35. export default defineComponent({
  36. name: 'suggestion-option',
  37. emits: ['close', 'submit'],
  38. setup(props, { emit, expose }) {
  39. const message = useMessage();
  40. const userStore = useUserStore();
  41. const forms = reactive({
  42. suggestionTypeId: null,
  43. clientType: 'TEACHER',
  44. content: '',
  45. attachmentUrls: '',
  46. type: 'APP',
  47. mobileNo: userStore.getUserInfo.phone
  48. });
  49. const state = reactive([]) as any;
  50. const isubmit = ref(false);
  51. const showSuggestion = ref(false);
  52. const suggestionTypeList = ref([] as any);
  53. const ossUploadUrl = `https://gyt.ks3-cn-beijing.ksyuncs.com/`;
  54. const uploadRef = ref();
  55. const fileListRef = ref<UploadFileInfo[]>([]);
  56. const formsRef = ref();
  57. const btnLoading = ref(false);
  58. const tempFiileBuffer = ref();
  59. const email = ref('');
  60. const phone = ref('');
  61. const isLoading = ref(false);
  62. const ishidden = ref(false);
  63. const onSubmit = async () => {
  64. // if (!forms.suggestionTypeId) {
  65. // message.error('请选择反馈类型');
  66. // return;
  67. // }
  68. // if (!forms.content) {
  69. // message.error('请填写反馈信息');
  70. // return;
  71. // }
  72. formsRef.value?.validate(async (err: any) => {
  73. if (err) {
  74. return;
  75. }
  76. const attachmentUrlsList = fileListRef.value.map((item: any) => {
  77. // console.log(item, 'item');
  78. // const name = item.name;
  79. // // const suffix = name.slice(name.lastIndexOf('.'));
  80. // const fileName = `${item.id + name}`;
  81. // const url = ossUploadUrl + fileName;
  82. return item.url;
  83. });
  84. const attachmentUrls = attachmentUrlsList.join(',');
  85. try {
  86. const res = await addSuggestion({ ...forms, attachmentUrls });
  87. isubmit.value = true;
  88. // message.success('提交成功');
  89. setTimeout(() => {
  90. onReset();
  91. ishidden.value = true;
  92. emit('close');
  93. }, 3000);
  94. } catch (e) {
  95. console.log(e);
  96. }
  97. console.log('onSubmit');
  98. });
  99. };
  100. const onReset = () => {
  101. ishidden.value = false;
  102. isubmit.value = false;
  103. forms.suggestionTypeId = null;
  104. forms.clientType = 'TEACHER';
  105. forms.content = '';
  106. forms.attachmentUrls = '';
  107. fileListRef.value = [];
  108. forms.type = 'APP';
  109. forms.mobileNo = userStore.getUserInfo.phone;
  110. };
  111. expose({ onReset });
  112. const onBeforeUpload = async (options: any) => {
  113. console.log(options, 'onBeforeUpload');
  114. const file = options.file;
  115. // 文件大小
  116. let isLt2M = true;
  117. const size = 2;
  118. if (size) {
  119. isLt2M = file.file.size / 1024 / 1024 < size;
  120. if (!isLt2M) {
  121. message.error(`文件大小不能超过${size}M`);
  122. return false;
  123. }
  124. }
  125. if (!isLt2M) {
  126. return isLt2M;
  127. }
  128. // 是否裁切
  129. try {
  130. btnLoading.value = true;
  131. const name = file.file.name;
  132. const fileName = `${file.id + name}`;
  133. const obj = {
  134. filename: fileName,
  135. bucketName: 'gyt',
  136. postData: {
  137. filename: fileName,
  138. acl: 'public-read',
  139. key: fileName,
  140. unknowValueField: []
  141. }
  142. };
  143. // const { data } = await policy(obj);
  144. const { data } = await getUploadSign(obj);
  145. state.push({
  146. id: file.id,
  147. tempFiileBuffer: file.file,
  148. policy: data.policy,
  149. signature: data.signature,
  150. acl: 'public-read',
  151. key: fileName,
  152. KSSAccessKeyId: data.kssAccessKeyId,
  153. name: fileName
  154. });
  155. } catch {
  156. //
  157. // message.error('上传失败')
  158. btnLoading.value = false;
  159. return false;
  160. }
  161. return true;
  162. };
  163. const onCustomRequest = ({
  164. file,
  165. // data,
  166. // headers,
  167. // withCredentials,
  168. action,
  169. onFinish,
  170. onError,
  171. onProgress
  172. }: UploadCustomRequestOptions) => {
  173. const item = state.find((c: any) => {
  174. return c.id == file.id;
  175. });
  176. item.file = file;
  177. onFileUpload({ file, action, data: item, onProgress, onFinish, onError });
  178. };
  179. const onFinish = (options: any) => {
  180. // const url =
  181. const name = options.file.name;
  182. // const suffix = name.slice(name.lastIndexOf('.'));
  183. const fileName = `${options.file.id + name}`;
  184. const url = ossUploadUrl + fileName;
  185. // urlList.value.push(url);
  186. // onFinishAfter(options);
  187. };
  188. const onRemove = async (event: any) => {
  189. console.log(event);
  190. btnLoading.value = false;
  191. };
  192. const getTypeList = async () => {
  193. try {
  194. const res = await getSuggestionList({ rows: 9999, page: 1 });
  195. suggestionTypeList.value = res.data.rows || [];
  196. } catch (e) {
  197. console.log(e);
  198. }
  199. };
  200. const getPhoneInfo = async () => {
  201. try {
  202. const { data } = await sysParamConfigPage({
  203. page: 1,
  204. rows: 999,
  205. group: 'OTHER'
  206. });
  207. const rows = data.rows || [];
  208. email.value = rows.find((item: any) => {
  209. return item.paramName == 'customer_service_email';
  210. }).paramValue;
  211. phone.value = rows.find((item: any) => {
  212. return item.paramName == 'customer_service_phone';
  213. }).paramValue;
  214. console.log(email.value, phone.value);
  215. } catch (e) {
  216. console.log('请求报错');
  217. console.log(e);
  218. }
  219. };
  220. const imglist = [inFront, inBack, submitBtn, sealing, boxBg];
  221. const loadImg = (imgList: any) => {
  222. for (let i = 0; i < imgList.length; i++) {
  223. const img = new Image();
  224. // let currentSrc = ''
  225. img.src = imgList[i];
  226. img.onload = function (e) {
  227. // console.log('加载完毕', e, img.complete);
  228. };
  229. img.onerror = function (e) {
  230. // console.log('加载错误', e);
  231. };
  232. }
  233. };
  234. loadImg(imglist);
  235. const suggestionStatus = ref(false);
  236. const getSuggestMessageUnread = async () => {
  237. try {
  238. const { data } = await suggestMessageUnread();
  239. const temp = data || [];
  240. temp.forEach((item: any) => {
  241. if (item.group === 'SYSTEM') {
  242. suggestionStatus.value = item.number > 0 ? true : false;
  243. }
  244. });
  245. } catch {
  246. //
  247. }
  248. };
  249. onMounted(() => {
  250. // getSuggestMessageUnread();
  251. getTypeList();
  252. getPhoneInfo();
  253. });
  254. return () => (
  255. <div class={[styles.suggestOption]}>
  256. <div
  257. class={[
  258. styles.updatePassword,
  259. isubmit.value ? styles.isAni : '',
  260. ishidden.value ? styles.isend : null
  261. ]}>
  262. <div class={[styles.formWrap, isubmit.value ? styles.isAni : '']}>
  263. <NImage
  264. class={styles.closeBtn}
  265. src={suggestClose}
  266. previewDisabled
  267. onClick={() => {
  268. onReset();
  269. emit('close');
  270. }}></NImage>
  271. <NImage class={styles.bgLine} src={bgLine} previewDisabled></NImage>
  272. <h2 class={styles.formTitle}>
  273. 意见反馈
  274. {/* suggestionStatus */}
  275. <NBadge
  276. dot={suggestionStatus.value}
  277. color={'#FF1036'}
  278. offset={[-5, 4]}
  279. class={styles.suggestionBtnDot}>
  280. <NButton
  281. type="primary"
  282. round
  283. secondary
  284. class={styles.suggestionBtn}
  285. onClick={() => (showSuggestion.value = true)}>
  286. 反馈记录
  287. </NButton>
  288. </NBadge>
  289. </h2>
  290. <div class={styles.formWrapInfo}>
  291. <NForm
  292. labelAlign="right"
  293. labelPlacement="left"
  294. labelWidth={'auto'}
  295. ref={formsRef}
  296. model={forms}
  297. requireMarkPlacement="left">
  298. {/* <NFormItem
  299. path="currentClass"
  300. label=""
  301. class={styles.phoneContainer}>
  302. <p class={styles.phone}>{forms.mobile}</p>
  303. </NFormItem> */}
  304. <NFormItem
  305. rule={[
  306. {
  307. required: true,
  308. message: '请选择反馈类型'
  309. }
  310. ]}
  311. path="suggestionTypeId">
  312. <CSelect
  313. class={styles.suggestSelect}
  314. value-field="id"
  315. label-field="name"
  316. style={{ width: '227px!important' }}
  317. {...({
  318. options: suggestionTypeList.value,
  319. placeholder: '反馈类型(必选)',
  320. clearable: true,
  321. inline: true
  322. } as any)}
  323. v-model:value={forms.suggestionTypeId}></CSelect>
  324. </NFormItem>
  325. <NFormItem
  326. path="content"
  327. rule={[
  328. {
  329. required: true,
  330. message: '请输入反馈内容'
  331. }
  332. ]}>
  333. <NInput
  334. class={styles.countInput}
  335. type="textarea"
  336. rows={5}
  337. placeholder={'请输入反馈内容'}
  338. maxlength={200}
  339. resizable={false}
  340. showCount
  341. v-model:value={forms.content}></NInput>
  342. </NFormItem>
  343. <NFormItem>
  344. <NUpload
  345. list-type="image-card"
  346. accept=".jpg,jpeg,.png"
  347. v-model:fileList={fileListRef.value}
  348. ref={uploadRef}
  349. multiple={true}
  350. max={5}
  351. // data={(file: any) => {
  352. // const item = state.find((c: any) => {
  353. // return c.id == file.file.id;
  354. // });
  355. // const { id, tempFiileBuffer, ...more } = item;
  356. // return { ...more };
  357. // }}
  358. showPreviewButton
  359. action={ossUploadUrl}
  360. customRequest={onCustomRequest}
  361. onBeforeUpload={(options: any) => onBeforeUpload(options)}
  362. onRemove={(options: any) => onRemove(options)}
  363. onFinish={(options: any) => onFinish(options)}>
  364. <div class={styles.addInput}>
  365. <NImage previewDisabled src={chioseAdd}></NImage>
  366. <p> 点击上传图片</p>
  367. <p>(最多五张)</p>
  368. </div>
  369. </NUpload>
  370. </NFormItem>
  371. {/* {phone.value || email.value ? (
  372. <NFormItem>
  373. <div class={styles.messageWrap}>
  374. {phone.value ? <p>客服电话:{phone.value}</p> : null}
  375. {email.value ? <p>邮箱:{email.value}</p> : null}
  376. </div>
  377. </NFormItem>
  378. ) : null} */}
  379. {/* <NSpace class={styles.updateBtnGroup}>
  380. <NButton
  381. strong
  382. type="default"
  383. round
  384. onClick={() => emit('close')}>
  385. 取消
  386. </NButton>
  387. <NButton strong type="primary" round onClick={() => onSubmit()}>
  388. 确认
  389. </NButton>
  390. </NSpace> */}
  391. </NForm>
  392. </div>
  393. </div>
  394. <div class={[styles.inBack, isubmit.value ? styles.isAni : '']}></div>
  395. {/* <div class={styles.inBackBottom}></div> boxBg */}
  396. <NImage
  397. src={boxBg}
  398. class={styles.inBackBottom}
  399. previewDisabled></NImage>
  400. <NImage src={inFront} class={styles.inFront} previewDisabled></NImage>
  401. <NImage
  402. src={sealing}
  403. class={[styles.sealing, isubmit.value ? styles.isAni : '']}
  404. previewDisabled></NImage>
  405. {!isubmit.value ? (
  406. <>
  407. <NImage
  408. src={submitBtn}
  409. onClick={() => {
  410. onSubmit();
  411. }}
  412. class={styles.submitBtn}
  413. previewDisabled></NImage>
  414. <div class={styles.messageWrap}>
  415. {phone.value ? <p>客服电话:{phone.value}</p> : null}
  416. {email.value ? <p>邮箱:{email.value}</p> : null}
  417. </div>
  418. </>
  419. ) : null}
  420. </div>
  421. <NModal
  422. maskClosable={modalClickMask}
  423. v-model:show={showSuggestion.value}
  424. class={['modalTitle background']}
  425. title={'反馈记录'}
  426. preset="card"
  427. closeOnEsc={false}
  428. style={{ width: '758px' }}>
  429. <SuggestionList typeList={suggestionTypeList.value} />
  430. </NModal>
  431. </div>
  432. );
  433. }
  434. });