quotationModal.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <!-- @format -->
  2. <script lang="ts" setup>
  3. import { Plus } from '@element-plus/icons-vue'
  4. import { getRFQsDetailApi, submitRFQsApiApi } from '@/api/model/my'
  5. import { ossUploadApi } from '@/api/model/common'
  6. import { downloadFileByA } from '@/utils/common/download'
  7. const props = defineProps({
  8. rfqId: {
  9. type: String,
  10. default: '',
  11. },
  12. })
  13. const emit = defineEmits(['update:data'])
  14. watch(() => props.rfqId, (val: any) => {
  15. if (val)
  16. getRFQsDetail(val)
  17. }, { immediate: true })
  18. const input = ref(null)
  19. const ruleFormRef = ref<any>()
  20. const loading = ref<boolean>(false)
  21. const ruleForm = ref<any>({
  22. productName: '',
  23. sourceQuantity: '',
  24. content: '',
  25. attachments: [],
  26. })
  27. const rules = ref<any>({
  28. productName: { required: true, message: 'Please input product name', trigger: 'blur' },
  29. sourceQuantity: { required: true, message: 'Please input sourcing quantity', trigger: 'blur' },
  30. content: { required: true, message: 'Please input detailed requirements', trigger: 'blur' },
  31. })
  32. const visible = defineModel('visible', { type: Boolean, required: true })
  33. function handleClose() {
  34. console.log('close')
  35. }
  36. async function getRFQsDetail(id: any) {
  37. try {
  38. const res: any = await getRFQsDetailApi({
  39. id,
  40. })
  41. ruleForm.value = res
  42. }
  43. catch (error) {
  44. console.log(error)
  45. }
  46. }
  47. function uploadData() {
  48. input.value?.click()
  49. }
  50. function inputChange(e: any) {
  51. const fileList = e.target.files
  52. const fileArr = Array.from(fileList)
  53. fileArr.forEach(async (file: any) => {
  54. const resourceUrl: any = await getResource(file)
  55. ruleForm.value.attachments.push({
  56. fileName: getFileTitle(resourceUrl),
  57. fileUrl: resourceUrl,
  58. })
  59. })
  60. }
  61. async function getResource(file: any) {
  62. try {
  63. if (file.size > 1024 * 1024 * 10)
  64. return ElMessage.error('Image size cannot exceed 10M')
  65. return await ossUploadApi(file)
  66. }
  67. catch (error) {
  68. ElMessage.error('send failed')
  69. }
  70. }
  71. function getFileTitle(file: string) {
  72. const fileArr = file.split('/')
  73. return fileArr[fileArr.length - 1]
  74. }
  75. function del(i: number) {
  76. ruleForm.value.attachments = ruleForm.value.attachments.filter((item, index) => index !== i)
  77. }
  78. async function submitForm(formEl: any | undefined) {
  79. if (!formEl)
  80. return
  81. await formEl.validate(async (valid, fields) => {
  82. if (valid) {
  83. loading.value = true
  84. await submitRFQsApiApi(ruleForm.value)
  85. ElMessage.success('submit success')
  86. visible.value = false
  87. loading.value = false
  88. emit('update:data')
  89. }
  90. else {
  91. console.log('error submit!', fields)
  92. loading.value = false
  93. }
  94. })
  95. }
  96. </script>
  97. <template>
  98. <el-dialog
  99. v-model="visible"
  100. :append-to-body="true"
  101. width="800"
  102. modal-class="custom-quotation-modal"
  103. @close="handleClose"
  104. >
  105. <template #header>
  106. <div
  107. class="px-40px py-25px bg-#F5F5F5 b-rd-lt-6px b-rd-rt-6px text-18px fw-500 text-#333"
  108. >
  109. Request For Quotation
  110. </div>
  111. </template>
  112. <div class="py-24px px-40px">
  113. <el-form
  114. ref="ruleFormRef"
  115. :model="ruleForm"
  116. :rules="rules"
  117. :disabled="!!rfqId"
  118. label-width="auto"
  119. class="custom-ruleForm"
  120. status-icon
  121. >
  122. <el-form-item label="Product Name" prop="productName">
  123. <el-input v-model="ruleForm.productName" placeholder="Please input product name" />
  124. </el-form-item>
  125. <el-form-item label="Sourcing quantity" prop="sourceQuantity">
  126. <el-input
  127. v-model="ruleForm.sourceQuantity"
  128. placeholder="Please input"
  129. >
  130. <template #append>
  131. Pcs
  132. </template>
  133. </el-input>
  134. </el-form-item>
  135. <el-form-item label="Detailed requirements" prop="content">
  136. <el-input
  137. v-model="ruleForm.content"
  138. :rows="4"
  139. type="textarea"
  140. placeholder="I 'm looking for..."
  141. />
  142. </el-form-item>
  143. </el-form>
  144. <div>
  145. <div v-if="!rfqId" class="flex items-center mb-16px">
  146. <el-button
  147. class="!bg-#C58C46 !text-#fff !ml-0 !w-250px !h-48px !text-14px !fw-500 !b-rd-6px"
  148. :icon="Plus"
  149. @click="uploadData"
  150. >
  151. Upload sourcing documents
  152. </el-button>
  153. <div class="ml-16px text-12px">
  154. Max file size: 10MB. Types supported: jpg, jpeg, png, pdf, docx, doc, xlsx, xls.
  155. </div>
  156. </div>
  157. <input
  158. ref="input"
  159. multiple="true"
  160. type="file"
  161. class="hidden"
  162. @change="inputChange"
  163. >
  164. <div>
  165. <div
  166. v-for="(items, index) in ruleForm.attachments"
  167. :key="index"
  168. class="py-14px px-16px bg-#F7F8FA flex items-center justify-between mt-8px b-solid b-1px b-#E0E0E0 b-rd-4px"
  169. >
  170. <div class="flex items-center">
  171. <svgo-file
  172. class="!w-20px cursor-pointer !h-20px text-#333 mr-16px"
  173. />
  174. 附件{{ index }}:
  175. <a :href="items.fileUrl"> {{ getFileTitle(items.fileUrl) }}</a>
  176. </div>
  177. <img v-if="!rfqId" src="@/assets/images/file_delete.png" class="!w-20px cursor-pointer !h-20px" alt="" srcset="" @click="del(index)">
  178. </div>
  179. </div>
  180. </div>
  181. </div>
  182. <template #footer>
  183. <el-button
  184. v-if="!rfqId"
  185. :loading="loading"
  186. class="!bg-#C58C46 !text-#fff !ml-0 !w-170px !h-48px !text-14px !fw-500 !b-rd-6px"
  187. @click="submitForm(ruleFormRef)"
  188. >
  189. Submit FRQ
  190. </el-button>
  191. <div v-if="rfqId">
  192. <div
  193. class="w-full py-24px !text-left px-16px b-dashed b-1px b-#E0E0E0 b-rd-4px "
  194. >
  195. <div v-if="ruleForm?.quotes">
  196. <div class="fw-700 text-#3D3D3D">
  197. Quotation for product
  198. </div>
  199. <div class="mt-20px mb-28px">
  200. <div v-for="item in ruleForm?.quotes?.attachments" :key="item.id" class="py-4px underline cursor-pointer" @click="downloadFileByA(item.fileUrl, item.fileName)">
  201. {{ getFileTitle(item.fileUrl) }}
  202. </div>
  203. </div>
  204. <div class="fw-700 my-20px text-#3D3D3D">
  205. Remark
  206. </div>
  207. <div class="text-#333">
  208. {{ ruleForm?.quotes?.remark }}
  209. </div>
  210. </div>
  211. <div v-else class="flex justify-center items-center">
  212. <svgo-quotation class="!w-28px !h-28px mr-14px" />
  213. No quotation yet
  214. </div>
  215. </div>
  216. </div>
  217. </template>
  218. </el-dialog>
  219. </template>
  220. <style lang="less">
  221. .custom-quotation-modal {
  222. .el-dialog {
  223. padding: unset !important;
  224. .el-dialog__header {
  225. padding: 0 !important;
  226. .el-dialog__headerbtn {
  227. right: 20px;
  228. top: 10px;
  229. }
  230. }
  231. .custom-ruleForm{
  232. .el-form-item{
  233. display: block;
  234. .el-form-item__label-wrap{
  235. margin-left: unset !important;
  236. margin-bottom: 5px;
  237. }
  238. .el-input__wrapper{
  239. .el-input__inner{
  240. height: 40px !important;
  241. }
  242. }
  243. .el-input-group--append{
  244. width: 480px!important;
  245. }
  246. }
  247. }
  248. .el-dialog__footer {
  249. padding-top: 24px !important;
  250. padding-bottom: 24px !important;
  251. padding-left: 40px !important;
  252. padding-right: 40px !important;
  253. border: 1px solid #E0E0E0;
  254. }
  255. }
  256. }
  257. </style>