edit.vue 18 KB

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