edit.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. <template>
  2. <div class="component-edit-indent">
  3. <el-dialog
  4. v-model="show"
  5. class="custom-edit-indent-dialog"
  6. :title="
  7. visible === 1
  8. ? $t(prefix_edit + 'title_add')
  9. : $t(prefix_edit + 'title_edit')
  10. "
  11. :close-on-click-modal="false"
  12. :close-on-press-escape="false"
  13. :before-close="close"
  14. width="960px"
  15. >
  16. <el-form
  17. ref="indentForm"
  18. :rules="computedRules"
  19. :model="form"
  20. label-width="130px"
  21. >
  22. <div class="edit-indent-form flex flex-wrap items-center">
  23. <el-form-item
  24. :label="$t(prefix + 'label_customer_name')"
  25. prop="custom_name"
  26. >
  27. <el-select
  28. v-model="form.custom_name"
  29. style="width: 100%"
  30. :placeholder="$t('text_please_select')"
  31. :remote-method="debounce(getCustomListFun, 500)"
  32. :loading="loading"
  33. remote
  34. filterable
  35. @change="customChange"
  36. >
  37. <!-- allow-create -->
  38. <el-option
  39. v-for="(option, index) in customList"
  40. :key="option.id || index"
  41. :label="option.name"
  42. :value="option.name"
  43. ></el-option>
  44. </el-select>
  45. </el-form-item>
  46. <el-form-item :label="$t(prefix + 'label_project_name')">
  47. <el-input v-model="form.deal_name"></el-input>
  48. </el-form-item>
  49. <el-form-item
  50. label="SKU"
  51. style="height: 40px"
  52. >
  53. <!-- <el-input
  54. style="display: none"
  55. v-model="form.product_sku"></el-input> -->
  56. <div
  57. class="el-input el-input__wrapper el-input__inner fake-sku-input"
  58. @click="skuSelectVisible = true"
  59. >
  60. {{ form.product_sku }}
  61. </div>
  62. </el-form-item>
  63. <el-form-item
  64. :label="$t(prefix + 'label_product_name')"
  65. prop="product_name"
  66. >
  67. <el-input
  68. v-model="form.product_name"
  69. disabled
  70. ></el-input>
  71. </el-form-item>
  72. <el-form-item
  73. v-if="/gi_/i.test(form.product_sku)"
  74. label="产品别名"
  75. prop="product_alias_name"
  76. >
  77. <el-input v-model="form.product_alias_name"></el-input>
  78. </el-form-item>
  79. <el-form-item v-if="/gi_/i.test(form.product_sku)">
  80. <div>&nbsp;</div>
  81. </el-form-item>
  82. <el-form-item
  83. :label="$t(prefix + 'label_require_material')"
  84. prop="require_material"
  85. >
  86. <div
  87. class="flex justify-between items-center"
  88. style="width: 100%"
  89. >
  90. <el-select v-model="form.require_material">
  91. <el-option
  92. v-for="item in materialOptions"
  93. :key="item.value"
  94. :label="item.label"
  95. :value="item.value"
  96. ></el-option>
  97. </el-select>
  98. <el-input
  99. v-if="form.require_material === '其它'"
  100. v-model.trim="form.require_material_other"
  101. placeholder="选其它时必填"
  102. style="width: 45%"
  103. ></el-input>
  104. </div>
  105. </el-form-item>
  106. <el-form-item
  107. :label="$t(prefix + 'label_category')"
  108. prop="product_category"
  109. >
  110. <div
  111. class="flex justify-between items-center"
  112. style="width: 100%"
  113. >
  114. <el-select v-model="form.product_category">
  115. <el-option
  116. v-for="item in categoryOptions"
  117. :key="item.value"
  118. :label="item.label"
  119. :value="item.value"
  120. ></el-option>
  121. </el-select>
  122. <el-input
  123. v-if="form.product_category === '其它'"
  124. v-model.trim="form.product_category_other"
  125. placeholder="选其它时必填"
  126. style="width: 45%"
  127. ></el-input>
  128. </div>
  129. </el-form-item>
  130. <el-form-item
  131. :label="$t(prefix + 'label_print_method')"
  132. prop="print_method"
  133. >
  134. <div
  135. class="flex justify-between items-center"
  136. style="width: 100%"
  137. >
  138. <el-select v-model="form.print_method">
  139. <el-option
  140. v-for="item in printOptions"
  141. :key="item.value"
  142. :label="item.label"
  143. :value="item.value"
  144. ></el-option>
  145. </el-select>
  146. <el-input
  147. v-if="form.print_method === '其它'"
  148. v-model.trim="form.print_method_other"
  149. placeholder="选其它时必填"
  150. style="width: 45%"
  151. ></el-input>
  152. </div>
  153. </el-form-item>
  154. <el-form-item :label="$t(prefix_edit + 'label_print_require')">
  155. <el-input v-model="form.require_print"></el-input>
  156. </el-form-item>
  157. <el-form-item
  158. :label="$t(prefix_edit + 'label_number')"
  159. prop="require_amount"
  160. >
  161. <el-input v-model="form.require_amount"></el-input>
  162. </el-form-item>
  163. <el-form-item :label="$t(prefix_edit + 'label_deliver_date')">
  164. <el-date-picker
  165. v-model="form.due_date"
  166. format="YYYY-MM-DD"
  167. value-format="YYYY-MM-DD"
  168. type="date"
  169. placeholder="选择日期"
  170. ></el-date-picker>
  171. </el-form-item>
  172. <el-form-item :label="$t(prefix_edit + 'label_ref_dimension')">
  173. <el-input
  174. v-model="form.ref_size_length"
  175. style="width: 50px"
  176. placeholder="长"
  177. ></el-input>
  178. <span>&nbsp;*&nbsp;</span>
  179. <el-input
  180. v-model="form.ref_size_width"
  181. style="width: 50px"
  182. placeholder="宽"
  183. ></el-input>
  184. <span>&nbsp;*&nbsp;</span>
  185. <el-input
  186. v-model="form.ref_size_height"
  187. style="width: 50px"
  188. placeholder="高"
  189. ></el-input>
  190. <span>&nbsp;CM</span>
  191. </el-form-item>
  192. <el-form-item
  193. :label="$t(prefix_edit + 'label_ref_url')"
  194. style="width: 98%"
  195. >
  196. <el-input v-model="form.ref_url"></el-input>
  197. </el-form-item>
  198. <el-form-item
  199. :label="$t(prefix_edit + 'label_comment')"
  200. style="width: 98%"
  201. >
  202. <el-input
  203. v-model="form.other_note"
  204. type="textarea"
  205. :rows="3"
  206. ></el-input>
  207. </el-form-item>
  208. <el-form-item
  209. :label="$t(prefix_edit + 'label_ref_image')"
  210. style="width: 100%"
  211. >
  212. <image-upload
  213. v-model:list="imageList"
  214. :disable-preview="true"
  215. ></image-upload>
  216. </el-form-item>
  217. <el-form-item style="width: 100%">
  218. <el-table
  219. v-show="quoteList.length"
  220. :data="quoteList"
  221. border
  222. stripe
  223. >
  224. <el-table-column
  225. width="200"
  226. :label="$t(prefix_edit + 'col_product_image')"
  227. >
  228. <template #default="scope">
  229. <div v-if="scope.row.product_image.length">
  230. <img
  231. :src="scope.row.product_image[0]"
  232. style="width: 100%; height: auto"
  233. />
  234. </div>
  235. </template>
  236. </el-table-column>
  237. <el-table-column
  238. width="200"
  239. prop="vendor_name"
  240. :label="$t(prefix_edit + 'col_supplier')"
  241. ></el-table-column>
  242. <el-table-column
  243. prop="product_url"
  244. label="URL"
  245. ></el-table-column>
  246. </el-table>
  247. <el-button
  248. type="warning"
  249. size="small"
  250. @click="addInfo"
  251. >
  252. + {{ $t(prefix_edit + 'btn_add_info') }}
  253. </el-button>
  254. </el-form-item>
  255. </div>
  256. </el-form>
  257. <template #footer>
  258. <div class="flex justify-center items-center">
  259. <el-button
  260. size="small"
  261. type="primary"
  262. :loading="commitLoading"
  263. @click="checkForm(indentForm)"
  264. >
  265. {{ $t('btn_submit') }}
  266. </el-button>
  267. <el-button
  268. size="small"
  269. type="primary"
  270. :loading="commitLoading"
  271. @click="checkForm(indentForm, false)"
  272. >
  273. {{ $t(prefix_edit + 'btn_submit_and_next') }}
  274. </el-button>
  275. </div>
  276. </template>
  277. </el-dialog>
  278. <editInfo
  279. v-model:visible="infoVisible"
  280. :default-name="form.product_alias_name || ''"
  281. :data-for-edit="quoteList"
  282. :parent-id="form.entity_id || 0"
  283. :default-sku="skuInfo"
  284. @create="quotaCreated"
  285. ></editInfo>
  286. <sku-select
  287. v-model:visible="skuSelectVisible"
  288. @select="selectSku"
  289. ></sku-select>
  290. </div>
  291. </template>
  292. <script lang="ts" setup>
  293. import { defineComponent, ref, watch, nextTick, inject, computed } from 'vue'
  294. import {
  295. ElButton,
  296. ElForm,
  297. ElFormItem,
  298. ElInput,
  299. ElTable,
  300. ElTableColumn,
  301. ElSelect,
  302. ElOption,
  303. ElDialog,
  304. ElNotification,
  305. ElDatePicker,
  306. } from 'element-plus'
  307. import type { FormInstance } from 'element-plus'
  308. import cloneDeep from 'lodash.clonedeep'
  309. import dayjs from 'dayjs'
  310. import debounce from 'lodash.debounce'
  311. import ImageUpload from '@/components/ImageUpload.vue'
  312. import editInfo from './components/info.vue'
  313. import skuSelect from './components/skuSelect.vue'
  314. import { $t } from '@/i18n/index'
  315. import { getCustomList, createIndent } from '@/api/indent'
  316. defineComponent({
  317. name: 'EditIndent',
  318. })
  319. const {
  320. visible = 0,
  321. quotaData = {} as any,
  322. indentData = {} as any,
  323. categoryOptions = [],
  324. printOptions = [],
  325. materialOptions = [],
  326. } = defineProps<{
  327. visible: number
  328. // 引用数据. 没有拼错, 不是quote询价信息列表. 这个数据是‘引用’业务克隆出来的, 虽然最终也会赋值给quoteList
  329. quotaData: object
  330. indentData: object
  331. categoryOptions: any[]
  332. printOptions: any[]
  333. materialOptions: any[]
  334. }>()
  335. watch(
  336. () => visible,
  337. () => {
  338. show.value = visible > 0
  339. if (visible > 0) {
  340. resetData()
  341. }
  342. if (visible === 1 && quotaData?.content?.length) {
  343. quotaCreated(quotaData)
  344. }
  345. if (visible === 2) {
  346. // 编辑. 初始化传进来的数据
  347. const temp = cloneDeep(indentData)
  348. if (temp.due_date) {
  349. temp.due_date = dayjs(temp.due_date * 1000).format('YYYY-MM-DD')
  350. } else {
  351. temp.due_date = ''
  352. }
  353. quoteList.value = cloneDeep(temp.lists || [])
  354. if (Array.isArray(temp.picture) && temp.picture.length) {
  355. nextTick(() => {
  356. imageList.value = temp.picture.map((img: string) => {
  357. return {
  358. url: $mediaRegExp.test(img)
  359. ? img
  360. : import.meta.env.VITE_APP_OSS_PREFIX + img,
  361. }
  362. })
  363. })
  364. }
  365. temp.entity_id = temp.id
  366. delete temp.id
  367. delete temp.admin_id
  368. delete temp.checked
  369. delete temp.create_time
  370. delete temp.creator
  371. delete temp.default_quote
  372. delete temp.lists // quoteList
  373. delete temp.update_time
  374. // delete temp.status
  375. form.value = Object.assign({}, cloneDeep(formDemo), temp)
  376. }
  377. },
  378. )
  379. const $emit = defineEmits(['update:visible', 'create'])
  380. const prefix = ref('order.indent.')
  381. const prefix_edit = ref('order.indent_edit.')
  382. const show = ref(false)
  383. const skuSelectVisible = ref(false)
  384. const form = ref({} as any)
  385. const formDemo = {
  386. custom_name: '',
  387. custom_id: '', // 联动custom_name
  388. deal_name: '',
  389. deal_id: '', // 永远是空
  390. product_sku: '',
  391. item_id: '', // 看旧代码是和 product_sku 有联动, 应该是商品的id
  392. product_name: '',
  393. product_alias_name: '',
  394. require_material: '',
  395. require_material_other: '',
  396. product_category: '',
  397. product_category_other: '',
  398. print_method: '',
  399. print_method_other: '',
  400. require_print: '',
  401. require_amount: '',
  402. due_date: '',
  403. ref_size_length: '',
  404. ref_size_width: '',
  405. ref_size_height: '',
  406. ref_url: '',
  407. other_note: '',
  408. picture: '',
  409. }
  410. const imageList = ref([])
  411. const checkMaterial = (rule: any, value: any, cb: any) => {
  412. if (value === '') {
  413. return cb(new Error($t('text_please_select')))
  414. } else if (value === '其它') {
  415. if (
  416. !(
  417. form.value.require_material_other &&
  418. form.value.require_material_other.length
  419. )
  420. ) {
  421. return cb(new Error('选择其它时, 需填写具体内容!'))
  422. }
  423. }
  424. cb()
  425. }
  426. const checkCategory = (rule: any, value: any, cb: any) => {
  427. if (value === '') {
  428. return cb(new Error($t('text_please_select')))
  429. } else if (value === '其它') {
  430. if (
  431. !(
  432. form.value.product_category_other &&
  433. form.value.product_category_other.length
  434. )
  435. ) {
  436. return cb(new Error('选择其它时, 需填写具体内容!'))
  437. }
  438. }
  439. cb()
  440. }
  441. const checkPrint = (rule: any, value: any, cb: any) => {
  442. if (value === '') {
  443. return cb(new Error($t('text_please_select')))
  444. } else if (value === '其它') {
  445. if (
  446. !(form.value.print_method_other && form.value.print_method_other.length)
  447. ) {
  448. return cb(new Error('选择其它时, 需填写具体内容!'))
  449. }
  450. }
  451. cb()
  452. }
  453. const rules = {
  454. custom_name: [
  455. {
  456. required: true,
  457. message: $t('text_please_input'),
  458. trigger: 'change',
  459. },
  460. ],
  461. custom_id: [
  462. {
  463. required: true,
  464. message: $t('text_please_input'),
  465. trigger: 'change',
  466. },
  467. ],
  468. product_name: [
  469. {
  470. required: true,
  471. message: '请通过sku表单项选择一个商品',
  472. trigger: 'change',
  473. },
  474. ],
  475. require_material: [
  476. { required: true, validator: checkMaterial, trigger: 'change' },
  477. ],
  478. product_category: [
  479. { required: true, validator: checkCategory, trigger: 'change' },
  480. ],
  481. print_method: [{ required: true, validator: checkPrint, trigger: 'change' }],
  482. require_amount: [
  483. {
  484. required: true,
  485. message: $t('text_please_select'),
  486. trigger: 'change',
  487. },
  488. ],
  489. }
  490. let computedRules = computed(() => {
  491. // product_sku 包含gi_的商品, 需要填写产品别名(必填), 否则返回rules的复制体
  492. let temp: any = cloneDeep(rules)
  493. if (/gi_/i.test(form.value.product_sku)) {
  494. temp.product_alias_name = [
  495. {
  496. required: true,
  497. message: '请填写产品别名',
  498. trigger: 'change',
  499. },
  500. ]
  501. }
  502. return temp
  503. })
  504. // 客户下拉框候选列表
  505. const customList = ref([] as any[])
  506. const loading = ref(false)
  507. const quoteList = ref([])
  508. let skuInfo = ref({} as any)
  509. const selectSku = (data: any) => {
  510. form.value.product_name = data.product_name || ''
  511. form.value.product_sku = data.product_sku || ''
  512. form.value.item_id = data.id || ''
  513. skuInfo.value = {
  514. Carton_Weight_Kg: data.Carton_Weight_Kg || '',
  515. Carton_Qty: data.Carton_Qty || '',
  516. Width: data.Width || '',
  517. Thickness_Height: data.Thickness_Height || '',
  518. Length: data.Length || '',
  519. }
  520. }
  521. const customChange = (value: any) => {
  522. const temp = customList.value.filter((i: any) => i.name === value)
  523. // id为空且custom_name有值是新增
  524. form.value.custom_id = temp.length ? temp[0].id : ''
  525. }
  526. // 成功创建了报价, 把数据展示到界面
  527. const quotaCreated = (data: any) => {
  528. if (Array.isArray(data.content)) {
  529. quoteList.value = data.content
  530. }
  531. ElNotification({
  532. type: 'warning',
  533. title: '报价数据已保存',
  534. message: '别忘了点击 提交 按钮以保存询价数据',
  535. duration: 10000,
  536. })
  537. }
  538. const resetData = () => {
  539. form.value = cloneDeep(formDemo)
  540. imageList.value = []
  541. customList.value = []
  542. quoteList.value = []
  543. }
  544. const close = (done = {} as any) => {
  545. $emit('update:visible', 0)
  546. if (typeof done === 'function') done()
  547. }
  548. const infoVisible = ref(0)
  549. const addInfo = () => {
  550. infoVisible.value = quoteList.value.length ? 2 : 1
  551. }
  552. let commitLoading = ref(false)
  553. const $mediaRegExp = inject('mediaRegExp') as RegExp
  554. const indentForm = ref<FormInstance>()
  555. const checkForm = (
  556. formEl: FormInstance | undefined,
  557. closeAfterCreate = true,
  558. ) => {
  559. if (!formEl || commitLoading.value) return
  560. formEl.validate((valid: boolean) => {
  561. if (valid) {
  562. form.value.picture = imageList.value
  563. .map((i: any) => {
  564. return i.url.replace($mediaRegExp, '/')
  565. })
  566. .join(',')
  567. // this$set(
  568. // this.form,
  569. // `quotes`,
  570. // quoteList.value.map((d: any) => d.entity_id || d.id),
  571. // )
  572. console.log(form.value, 'form.value')
  573. form.value.quotes = quoteList.value.map((d: any) => d.entity_id || d.id)
  574. commitLoading.value = true
  575. createIndent(form.value)
  576. .then((response: any) => {
  577. // console.log(response, 'create indent')
  578. if (response.code === 1) {
  579. ElNotification({
  580. title: '创建成功',
  581. message: '正在刷新数据',
  582. duration: 3000,
  583. })
  584. // notify parent-component to refresh list data
  585. $emit('create')
  586. resetData()
  587. if (closeAfterCreate) {
  588. close()
  589. } else {
  590. formEl.clearValidate()
  591. }
  592. }
  593. })
  594. .catch(() => {
  595. ElNotification({
  596. duration: 3000,
  597. title: '创建出错',
  598. message:
  599. '请稍后再试. 或者按F12打开控制台, 把错误信息复制出来找管理员排查.',
  600. })
  601. })
  602. .finally(() => {
  603. commitLoading.value = false
  604. })
  605. } else {
  606. commitLoading.value = false
  607. }
  608. })
  609. }
  610. const getCustomListFun = (keyword: string) => {
  611. let keywords = keyword.trim()
  612. if (!keywords.length) return
  613. loading.value = true
  614. customList.value = []
  615. getCustomList({ keywords })
  616. .then((response: any) => {
  617. const defaultCreateOption = {
  618. name: keywords,
  619. id: '',
  620. }
  621. if (Array.isArray(response.result)) {
  622. const tempStr = keywords.replace(/\S_-/g, '').toLowerCase()
  623. const tempList = response.result.map((i: any) =>
  624. i.name.replace(/\S_-/g, '').toLowerCase(),
  625. )
  626. let result = []
  627. if (response.result.length) {
  628. if (tempList.includes(tempStr)) {
  629. result = response.result
  630. } else {
  631. // 结果里面没找到输入的搜索字符串, 就把它加入到结果里面当成‘新增的’
  632. result = [cloneDeep(defaultCreateOption)].concat(response.result)
  633. }
  634. } else if (keywords.length) {
  635. result = [cloneDeep(defaultCreateOption)]
  636. }
  637. customList.value = result
  638. }
  639. })
  640. .finally(() => {
  641. loading.value = false
  642. })
  643. }
  644. getCustomListFun('')
  645. </script>
  646. <style lang="scss">
  647. .component-edit-indent {
  648. .custom-edit-indent-dialog {
  649. margin-top: 0 !important;
  650. margin-bottom: 0 !important;
  651. height: 100vh;
  652. .el-dialog__body {
  653. max-height: 87vh;
  654. overflow-y: scroll;
  655. overflow-x: auto;
  656. padding-top: 8px;
  657. }
  658. }
  659. }
  660. </style>
  661. <style lang="scss" scoped>
  662. .edit-indent-form {
  663. .el-form-item {
  664. width: 49%;
  665. }
  666. }
  667. .fake-sku-input {
  668. height: 30px;
  669. cursor: pointer;
  670. }
  671. </style>