quoteRecord.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. <template>
  2. <div class="dialog-change-order">
  3. <el-dialog
  4. v-model="show"
  5. class="custom-change-order-dialog"
  6. title="报价记录"
  7. :close-on-click-modal="false"
  8. :close-on-press-escape="false"
  9. :before-close="close"
  10. width="1200px"
  11. >
  12. <el-table
  13. v-loading="loading"
  14. :expand-row-keys="expandRowKeys"
  15. row-key="id"
  16. :data="list"
  17. >
  18. <el-table-column type="expand">
  19. <template #default="props">
  20. <el-form
  21. :ref="($el) => setFormRef($el, props.row.id_string)"
  22. :model="formList[props.$index]"
  23. label-width="140"
  24. >
  25. <div class="flex justify-between flex-wrap">
  26. <el-form-item
  27. :rules="{
  28. required: true,
  29. }"
  30. prop="product_sku"
  31. label="SKU"
  32. style="height: 40px"
  33. >
  34. <div
  35. class="el-input el-input__wrapper el-input__inner fake-sku-input"
  36. @click="openSkuSelect(props.$index)"
  37. >
  38. <el-input
  39. style="display: none"
  40. v-model="formList[props.$index].product_sku"
  41. ></el-input>
  42. <span class="cursor-pointer">
  43. {{ formList[props.$index].product_sku }}
  44. </span>
  45. </div>
  46. </el-form-item>
  47. <el-form-item
  48. label="产品名称"
  49. prop="product_name"
  50. >
  51. <el-input
  52. v-model="formList[props.$index].product_name"
  53. disabled
  54. ></el-input>
  55. </el-form-item>
  56. <el-form-item
  57. :rules="{
  58. required: true,
  59. }"
  60. label="交付日期"
  61. prop="Expected_Delivery_Date"
  62. >
  63. <el-date-picker
  64. v-model="formList[props.$index].Expected_Delivery_Date"
  65. format="YYYY-MM-DD"
  66. value-format="YYYY-MM-DD"
  67. type="date"
  68. ></el-date-picker>
  69. </el-form-item>
  70. <el-form-item
  71. :rules="{
  72. required: true,
  73. }"
  74. label="Brand Name"
  75. prop="Brand_Name"
  76. >
  77. <el-input
  78. v-model="formList[props.$index].Brand_Name"
  79. ></el-input>
  80. </el-form-item>
  81. <el-form-item
  82. :rules="{
  83. required: true,
  84. }"
  85. label="Client Mailbox"
  86. prop="Client_Mailbox"
  87. >
  88. <el-input
  89. v-model="formList[props.$index].Client_Mailbox"
  90. ></el-input>
  91. </el-form-item>
  92. <el-form-item
  93. :rules="{
  94. required: true,
  95. message: 'Job Name is required',
  96. }"
  97. label="Job Name"
  98. prop="Contract_Title"
  99. >
  100. <el-input
  101. v-model="formList[props.$index].Contract_Title"
  102. ></el-input>
  103. </el-form-item>
  104. <el-form-item
  105. label="ATTN"
  106. prop="Primary_Contact"
  107. >
  108. <el-input v-model="formList[props.$index].Primary_Contact">
  109. <template #append>
  110. <el-button @click="openATTNDialog(props.$index)">
  111. <el-icon :size="14">
  112. <Notebook></Notebook>
  113. </el-icon>
  114. </el-button>
  115. </template>
  116. </el-input>
  117. </el-form-item>
  118. <el-form-item
  119. label="Phone"
  120. prop="Phone"
  121. >
  122. <el-input v-model="formList[props.$index].Phone"></el-input>
  123. </el-form-item>
  124. <el-form-item
  125. :rules="{
  126. required: true,
  127. }"
  128. label-width="220"
  129. label="Shipping Unit/Building Name"
  130. prop="Shipping_Unit_Building_Name"
  131. >
  132. <el-input
  133. v-model="formList[props.$index].Shipping_Unit_Building_Name"
  134. ></el-input>
  135. <!-- <el-select
  136. v-model="formList[props.$index].Shipping_Unit_Building_Name"
  137. allow-create
  138. filterable
  139. remote
  140. placeholder="enter directly or select a place"
  141. :remote-method="($e) => remoteSearchPlace($e, props.$index)"
  142. :loading="loading"
  143. >
  144. <el-option
  145. v-for="item in remoteOption[props.$index] as any[]"
  146. :key="item.value"
  147. :label="item.label"
  148. :value="item.value"
  149. />
  150. </el-select> -->
  151. </el-form-item>
  152. <el-form-item
  153. label="Shipping Street"
  154. prop="Shipping_Street"
  155. >
  156. <el-input
  157. v-model="formList[props.$index].Shipping_Street"
  158. ></el-input>
  159. </el-form-item>
  160. <el-form-item
  161. label="Shipping City"
  162. prop="Shipping_City"
  163. >
  164. <el-input
  165. v-model="formList[props.$index].Shipping_City"
  166. ></el-input>
  167. </el-form-item>
  168. <el-form-item
  169. label="Shipping State"
  170. prop="Shipping_State"
  171. >
  172. <el-input
  173. v-model="formList[props.$index].Shipping_State"
  174. ></el-input>
  175. </el-form-item>
  176. <el-form-item
  177. label="Shipping Code"
  178. prop="Shipping_Code"
  179. >
  180. <el-input
  181. v-model="formList[props.$index].Shipping_Code"
  182. ></el-input>
  183. </el-form-item>
  184. <el-form-item
  185. :rules="{
  186. required: true,
  187. }"
  188. label="Shipping Country"
  189. prop="Shipping_Country"
  190. >
  191. <el-input
  192. v-model="formList[props.$index].Shipping_Country"
  193. ></el-input>
  194. </el-form-item>
  195. <el-form-item
  196. label="Tax Level"
  197. :rules="{
  198. required: true,
  199. }"
  200. prop="Tax_Level"
  201. >
  202. <el-select v-model="formList[props.$index].Tax_Level">
  203. <el-option
  204. v-for="item in taxList"
  205. :key="item"
  206. :label="item"
  207. :value="item"
  208. ></el-option>
  209. </el-select>
  210. </el-form-item>
  211. <el-form-item
  212. label="客户名称"
  213. prop="custom_name"
  214. :rules="{
  215. required: true,
  216. }"
  217. >
  218. <el-select
  219. v-model="formList[props.$index].custom_name"
  220. style="width: 100%"
  221. :placeholder="$t('text_please_select')"
  222. :remote-method="debounce(getCustomListFun, 500)"
  223. :loading="customLoading"
  224. remote
  225. filterable
  226. @change="selectCustomer"
  227. >
  228. <!-- :disabled="!!indentData.enquiry_id" -->
  229. <el-option
  230. v-for="(option, index) in customList"
  231. :key="option.id || index"
  232. :label="option.name"
  233. :value="option.name"
  234. ></el-option>
  235. </el-select>
  236. </el-form-item>
  237. </div>
  238. <div class="flex justify-end">
  239. <el-button
  240. size="small"
  241. @click="expandRowKeys = []"
  242. >
  243. 取消
  244. </el-button>
  245. <el-button
  246. size="small"
  247. type="primary"
  248. @click="submitChangeOrder(props.$index)"
  249. >
  250. 提交
  251. </el-button>
  252. <!-- <el-button @click="toApplyVendor">test</el-button> -->
  253. </div>
  254. </el-form>
  255. </template>
  256. </el-table-column>
  257. <el-table-column
  258. label="序号"
  259. width="65"
  260. type="index"
  261. ></el-table-column>
  262. <el-table-column
  263. min-width="180"
  264. max-width="300"
  265. label="Items"
  266. prop="items"
  267. ></el-table-column>
  268. <el-table-column
  269. label="采购数量"
  270. prop="number"
  271. ></el-table-column>
  272. <el-table-column
  273. label="售价"
  274. prop="unit_cost"
  275. ></el-table-column>
  276. <el-table-column
  277. label="setup"
  278. prop="setup_cost"
  279. ></el-table-column>
  280. <el-table-column
  281. label="运费"
  282. prop="freight"
  283. ></el-table-column>
  284. <el-table-column
  285. width="180"
  286. label="创建时间"
  287. prop="create_time"
  288. ></el-table-column>
  289. <el-table-column label="操作">
  290. <template #default="scope">
  291. <el-button
  292. size="small"
  293. type="primary"
  294. plain
  295. @click="expandRowKeys.push(scope.row.id_string)"
  296. >
  297. 转单
  298. </el-button>
  299. </template>
  300. </el-table-column>
  301. </el-table>
  302. <div class="my-4 flex justify-end">
  303. <el-pagination
  304. v-model:current-page="currentPage"
  305. v-model:page-size="pageSize"
  306. v-model:total="total"
  307. layout="prev, pager, next, jumper, sizes"
  308. :page-sizes="[10, 20, 50, 100]"
  309. @current-change="getCalcPriceRecordFunc"
  310. @size-change="getCalcPriceRecordFunc"
  311. ></el-pagination>
  312. </div>
  313. </el-dialog>
  314. <el-dialog
  315. v-model="show2"
  316. class="attn-dialog"
  317. title="选择地址"
  318. :close-on-click-modal="false"
  319. :close-on-press-escape="false"
  320. width="1150px"
  321. >
  322. <el-table
  323. v-if="currentATTNList"
  324. highlight-current-row
  325. :data="currentATTNList"
  326. max-height="70vh"
  327. @current-change="selectATTN"
  328. >
  329. <el-table-column
  330. type="index"
  331. width="50"
  332. ></el-table-column>
  333. <el-table-column
  334. width="150"
  335. label="ATTN"
  336. prop="Primary_Contact"
  337. ></el-table-column>
  338. <el-table-column
  339. width="120"
  340. label="Phone"
  341. prop="Phone"
  342. ></el-table-column>
  343. <el-table-column
  344. label="Shipping_Unit_Building_Name"
  345. prop="Shipping_Unit_Building_Name"
  346. ></el-table-column>
  347. <el-table-column
  348. label="Shipping_Street"
  349. prop="Shipping_Street"
  350. ></el-table-column>
  351. <el-table-column
  352. width="135"
  353. label="Shipping_State"
  354. prop="Shipping_State"
  355. ></el-table-column>
  356. </el-table>
  357. <div class="flex justify-center pt-2">
  358. <el-button
  359. type="primary"
  360. @click="onATTNSelect"
  361. >
  362. 选择该项进行填充
  363. </el-button>
  364. <el-button @click="show2 = false">关闭</el-button>
  365. </div>
  366. </el-dialog>
  367. <sku-select
  368. v-model:visible="skuSelectVisible"
  369. @select="selectSku"
  370. ></sku-select>
  371. </div>
  372. </template>
  373. <script lang="ts" setup>
  374. import { defineComponent, ref, watch } from 'vue'
  375. import {
  376. ElDialog,
  377. ElButton,
  378. ElTable,
  379. ElTableColumn,
  380. ElNotification,
  381. ElPagination,
  382. ElMessage,
  383. ElMessageBox,
  384. ElForm,
  385. ElFormItem,
  386. ElInput,
  387. ElDatePicker,
  388. ElSelect,
  389. ElOption,
  390. ElIcon,
  391. } from 'element-plus'
  392. import { Notebook } from '@element-plus/icons-vue'
  393. import Cookie from 'js-cookie'
  394. import { getCalcPriceRecord, generateOrder } from '@/api/indent'
  395. import debounce from 'lodash.debounce'
  396. import { $t } from '@/i18n/index'
  397. import { getCustomList } from '@/api/indent'
  398. import userAPI from '@/api/user'
  399. import skuSelect from './skuSelect.vue'
  400. defineComponent({
  401. name: 'ComponentQuoteRecord',
  402. })
  403. const {
  404. visible = false,
  405. id = '',
  406. indentData = {} as any,
  407. } = defineProps<{
  408. visible: boolean
  409. id: string | number
  410. indentData: object
  411. }>()
  412. const $emit = defineEmits(['update:visible', 'success'])
  413. let show = ref(false)
  414. let show2 = ref(false)
  415. let loading = ref(false)
  416. let list = ref([] as any[])
  417. // let remoteOption = ref([])
  418. let user: any = ref({} as any)
  419. const getUserDetailFunc = () => {
  420. let indentID = Cookie.get('indent-id') || ''
  421. let indentName = Cookie.get('indent-user') || ''
  422. console.log(indentID, 'indentID')
  423. console.log(indentName, 'indentName')
  424. // 由于历史原因, 早期登录时没有记录下用户id, 只有用户名称.
  425. if (indentID) {
  426. userAPI.getUserDetail({ id: indentID }).then((res: any) => {
  427. if (res.code === 1) {
  428. user.value = res.result || {}
  429. console.log(res.result, 'res')
  430. }
  431. })
  432. } else if (indentName) {
  433. // get user by name
  434. userAPI.getUserDetailByName({ username: indentName }).then((res: any) => {
  435. if (res.code === 1) {
  436. user.value = res.result || {}
  437. console.log(res.result, 'res')
  438. }
  439. })
  440. }
  441. }
  442. watch(
  443. () => visible,
  444. () => {
  445. show.value = visible
  446. if (show.value) {
  447. getCalcPriceRecordFunc()
  448. getUserDetailFunc()
  449. }
  450. },
  451. )
  452. const pageSize = ref(10)
  453. const currentPage = ref(1)
  454. const total = ref(0)
  455. const taxList = ref(['GST AU 10%', 'GST NZ 15%', 'No GST 0%', 'VAT 20%'])
  456. const getCalcPriceRecordFunc = () => {
  457. loading.value = true
  458. getCalcPriceRecord({ id: id, limit: pageSize.value, page: currentPage.value })
  459. .then((res: any) => {
  460. if (res.code === 1)
  461. list.value =
  462. res.result.data.map((i: any) => ({
  463. ...i,
  464. id_string: `${i.id}`,
  465. })) || []
  466. for (let i = 0; i < list.value.length; i++) {
  467. const v = list.value[i].Accounts || ({} as any)
  468. // remoteOption.value.push([])
  469. formList.value.push({
  470. // custom_id 不存在说明是旧系统的数据, 需要置空让用户重新选客户
  471. custom_name: indentData.custom_id ? indentData.custom_name : '',
  472. custom_id: indentData.custom_id || '', // 联动custom_name
  473. item_id: indentData.item_id || '',
  474. product_name: indentData.product_name || '',
  475. product_sku: indentData.product_sku || '',
  476. Contract_Title: '',
  477. Expected_Delivery_Date: '',
  478. Brand_Name: '',
  479. Client_Mailbox: '',
  480. Shipping_Unit_Building_Name: v.Billing_Unit_Building_Name || '',
  481. Shipping_Street: v.Shipping_Street || '',
  482. Shipping_City: v.Shipping_City || '',
  483. Shipping_State: v.Shipping_State || '',
  484. Shipping_Code: v.Shipping_Code || '',
  485. Shipping_Country: v.Shipping_Country || '',
  486. Tax_Level: 'GST AU 10%',
  487. Order_Source: 'Indent App',
  488. Contact_Name: { id: '4791186000057250001', name: '/' },
  489. Primary_Contact: '',
  490. Phone: '',
  491. })
  492. }
  493. total.value = res.result.total || 0
  494. })
  495. .finally(() => (loading.value = false))
  496. }
  497. const currentATTNList = ref([] as any[])
  498. let currentATTNIndex = ref(0) // 当前第几行表单在选attn
  499. const openATTNDialog = (index: number) => {
  500. show2.value = true
  501. currentATTNList.value = list.value[index].AccountEndUser || []
  502. currentATTN.value = {}
  503. console.log(index, 'index')
  504. currentATTNIndex.value = index || 0
  505. }
  506. let currentATTN = ref({} as any)
  507. const selectATTN = (value: any) => {
  508. currentATTN.value = value || {}
  509. }
  510. const onATTNSelect = () => {
  511. if (!currentATTN.value.id) {
  512. ElMessage.error('请先点击选择一行地址数据, 再点击按钮')
  513. return
  514. }
  515. const v = currentATTN.value
  516. const target = formList.value[currentATTNIndex.value]
  517. target.Shipping_Unit_Building_Name = v.Shipping_Unit_Building_Name || ''
  518. target.Shipping_Street = v.Shipping_Street || ''
  519. target.Shipping_City = v.Shipping_City || ''
  520. target.Shipping_State = v.Shipping_State || ''
  521. target.Shipping_Code = v.Shipping_Post_Code || ''
  522. target.Shipping_Country = v.Shipping_Country || ''
  523. target.Primary_Contact = v.Primary_Contact || ''
  524. target.Phone = v.Phone || ''
  525. show2.value = false
  526. }
  527. let close = (done = {} as any) => {
  528. $emit('update:visible', false)
  529. list.value = []
  530. formList.value = []
  531. // remoteOption.value = []
  532. if (typeof done === 'function') done()
  533. }
  534. const expandRowKeys = ref([] as string[])
  535. const formList = ref([] as any[])
  536. const formListRef = ref({} as any)
  537. const setFormRef = (el: any, key: string) => {
  538. formListRef.value[`${key}`] = el
  539. }
  540. const submitChangeOrder = (index: number) => {
  541. const key = list.value[index].id_string
  542. const target: any = formListRef.value[`${key}`]
  543. target.validate((valid: boolean) => {
  544. if (!valid) return
  545. const data = Object.assign({}, formList.value[index], {
  546. id: list.value[index].id,
  547. })
  548. if (/gi_/i.test(data.product_sku)) {
  549. ElNotification({
  550. title: '注意',
  551. message: '请选择正确的SKU进行转单',
  552. duration: 5000,
  553. })
  554. return
  555. }
  556. if (!data.product_sku || !data.product_name) {
  557. ElNotification({
  558. title: 'SKU/产品名称不能为空',
  559. message: '请选择正确的SKU进行转单',
  560. duration: 5000,
  561. })
  562. return
  563. }
  564. if (!data.custom_id || !data.custom_name) {
  565. ElNotification({
  566. title: '客户名称 数据出错',
  567. message: '如果界面上的客户名称有值, 请联系管理员',
  568. duration: 5000,
  569. })
  570. return
  571. }
  572. console.log(data, 'data')
  573. onChangeOrderClick(data)
  574. })
  575. }
  576. // 跳转到申请录入供应商的外部页面.
  577. const toApplyVendor = () => {
  578. // 找出当前转单的记录. 必定存在, 不然进不来这个组件.
  579. let a = indentData.lists.filter((i: any) => i.id === id)
  580. let tt = a[0] || {}
  581. console.log(tt, 'tt')
  582. // 为空或者不是纯数字, 说明这个id不是真的在crm的供应商id
  583. if ([null, 'null', ''].includes(tt.vendor_id) || !/\d+/.test(tt.vendor_id)) {
  584. window.open(
  585. `https://forms.zohopublic.com/promocollectionau/form/SupplierCreationRequirements/formperma/ArflvTSXGDW--ONLT9Wgo5t0N1XLRJ8wGErjacQZl5g?email=${user.value.email || ''}&vendor=${encodeURIComponent(tt.vendor_name)}&name=${encodeURIComponent(tt.vendor_contact)}&phone=${tt.vendor_phone}`,
  586. )
  587. }
  588. }
  589. const onChangeOrderClick = (row: any) => {
  590. ElMessageBox.confirm('选择该条计价记录进行转单, 是否继续?', '提示', {
  591. confirmButtonText: '确定',
  592. cancelButtonText: '取消',
  593. type: 'warning',
  594. }).then(() => {
  595. loading.value = true
  596. generateOrder(row)
  597. .then((res: any) => {
  598. console.log(res, 'res')
  599. if (res.code === 1) {
  600. ElNotification({
  601. title: '转单成功',
  602. message: '正在刷新数据',
  603. duration: 5000,
  604. })
  605. $emit('success')
  606. let temp = res.result.data
  607. if (Array.isArray(temp) && temp.length) {
  608. let id = temp[0].details?.id
  609. window.open(
  610. import.meta.env.VITE_SO_PATH +
  611. `${id}` +
  612. import.meta.env.VITE_SO_APPEND,
  613. )
  614. toApplyVendor()
  615. }
  616. close() // 尝试处理部分浏览器(360)不会自动触发close回调的问题.
  617. } else {
  618. ElNotification({
  619. title: '转单失败',
  620. message: res.message + ', ' + row.toString(),
  621. })
  622. }
  623. })
  624. .finally(() => (loading.value = false))
  625. })
  626. }
  627. // const remoteSearchPlace = (str: string, index: number) => {
  628. // console.log(str, 'str', index)
  629. // if (!str.length) return
  630. // const target = remoteOption.value[index] as any[]
  631. // if (target && Array.isArray(target)) {
  632. // target.push({ value: 1, label: 1 })
  633. // }
  634. // }
  635. const skuSelectVisible = ref(false)
  636. const openSkuSelect = (key: any) => {
  637. skuSelectVisible.value = true
  638. }
  639. const selectSku = (data: any) => {
  640. formList.value = formList.value.map((i: any) => {
  641. return {
  642. ...i,
  643. product_name: data.product_name || '',
  644. product_sku: data.product_sku || '',
  645. item_id: data.id || '',
  646. }
  647. })
  648. }
  649. // 客户下拉框候选列表
  650. const customList = ref([] as any[])
  651. let customLoading = ref(false)
  652. const selectCustomer = (data: any) => {
  653. const temp = customList.value.filter((i: any) => i.name === data)
  654. if (temp.length === 0) {
  655. ElMessage.error('客户名称不存在, 请重新选择')
  656. return
  657. }
  658. formList.value = formList.value.map((i: any) => {
  659. return {
  660. ...i,
  661. custom_name: temp[0].name || '',
  662. custom_id: temp[0].id || '',
  663. }
  664. })
  665. }
  666. const getCustomListFun = (keyword: string) => {
  667. let keywords = keyword.trim()
  668. if (!keywords.length) return
  669. customLoading.value = true
  670. customList.value = []
  671. getCustomList({ keywords })
  672. .then((response: any) => {
  673. if (Array.isArray(response.result)) {
  674. customList.value = response.result.length ? response.result : []
  675. }
  676. })
  677. .finally(() => {
  678. customLoading.value = false
  679. })
  680. }
  681. </script>
  682. <style lang="scss">
  683. .dialog-change-order {
  684. .el-form-item {
  685. width: 49%;
  686. }
  687. .attn-dialog {
  688. margin-top: 8vh;
  689. }
  690. }
  691. </style>
  692. <style lang="scss">
  693. .custom-change-order-dialog {
  694. margin-top: 7vh;
  695. }
  696. </style>