EditModal.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <template>
  2. <el-dialog :visible.sync="visible" v-bind="modalOptions" append-to-body>
  3. <div class="tree-container">
  4. <el-tree
  5. :data="[ formData ]" :props="{ children: 'childs' }" node-key="orderNumber" default-expand-all
  6. draggable
  7. :expand-on-click-node="false"
  8. >
  9. <div slot-scope="{ node, data }">
  10. <div style="display: flex;align-items: center;justify-content: space-between;padding: 10px 0;">
  11. <div v-if="data.depth <= 3" style="margin-right: 14px;">
  12. <el-input
  13. v-model="data.groupName" maxlength="10" size="mini" style="width: 280px;"
  14. :placeholder="{ 1: '输入一级分组名称(最大6个字符)', 2: '输入二级分组名称(最大6个字符)', 2: '输入三级分组名称(最大10个字符)' }[data.depth]"
  15. />
  16. <el-input v-model="data.groupDescribe" size="mini" style="width: 340px;" placeholder="请输入分组描述" />
  17. </div>
  18. <div>
  19. <el-button
  20. type="text" size="mini"
  21. @click="$refs.RelatedGroupProducts && $refs.RelatedGroupProducts.handleOpen(data.products)"
  22. >
  23. 分组商品详情
  24. </el-button>
  25. <el-popover placement="top" width="330" trigger="hover" style="margin: 0 8px;">
  26. <div style="color: #000000;">请选择添加方式{{ data.selectType }}</div>
  27. <!-- <div>
  28. <el-button
  29. type="text" size="mini"
  30. @click="(data.selectType = 1) && handleAddProduct(data, node)"
  31. >
  32. <span>
  33. 手动选择
  34. <span v-if="data.ids && data.ids.length">
  35. (已选{{ data.ids.length }}个)
  36. </span>
  37. </span>
  38. </el-button>
  39. <el-button
  40. type="text" size="mini"
  41. @click="(data.selectType = 2) && (selectedGroupId = data.orderNumber) && $refs.IntelProductScreening && $refs.IntelProductScreening.handleOpen(data)"
  42. >
  43. <span>
  44. 智能添加
  45. <span v-if="data.conditions && data.conditions.length">
  46. (已设置)
  47. </span>
  48. </span>
  49. </el-button>
  50. </div> -->
  51. <div class="add-group-container">
  52. <el-radio-group
  53. v-model="data.selectType" size="mini" style="margin-top: 6px;"
  54. >
  55. <el-radio-button :label="1">
  56. <div style="padding: 6px 16px" @click="((data.selectType = 1) && $forceUpdate()) || handleAddProduct(data, node)">
  57. 手动选择
  58. <span v-if="data.ids && data.ids.length">
  59. (已选{{ data.ids.length }}个)
  60. </span>
  61. </div>
  62. </el-radio-button>
  63. <el-radio-button :label="2">
  64. <div style="padding: 6px 16px" @click="((data.selectType = 2) && $forceUpdate()) || (selectedGroupId = data.orderNumber) && $refs.IntelProductScreening && $refs.IntelProductScreening.handleOpen(data)">
  65. 智能添加
  66. <span v-if="data.conditions && data.conditions.length">
  67. (已设置)
  68. </span>
  69. </div>
  70. </el-radio-button>
  71. </el-radio-group>
  72. </div>
  73. <el-button slot="reference" type="text" size="mini">
  74. 添加分组商品
  75. </el-button>
  76. </el-popover>
  77. <el-button v-if="data.depth < 3" type="text" size="mini" @click="handleAppend(data)">
  78. {{ { 1: '添加二级分组名称', 2: '添加三级分组名称' }[data.depth] }}
  79. </el-button>
  80. <el-button v-if="data.depth > 1" type="text" size="mini" @click="handleDelete(data, node)">
  81. 删除
  82. </el-button>
  83. </div>
  84. </div>
  85. </div>
  86. </el-tree>
  87. <div>
  88. <el-button type="primary" size="small" @click="handleSubmit">
  89. 保存
  90. </el-button>
  91. <el-button type="danger" size="small" @click="handleClose">
  92. 取消
  93. </el-button>
  94. </div>
  95. <el-dialog
  96. class="product-dialog" title="手动添加商品" :visible.sync="isShowManualAdd" width="900px"
  97. top="50px"
  98. append-to-body
  99. >
  100. <div class="filter-container" style="display: flex;align-items: center;flex-wrap: wrap;">
  101. <el-input
  102. v-model="listQuery.search" clearable size="mini" class="filter-item"
  103. style="width: 200px;"
  104. placeholder="搜索商品名称或商品ID"
  105. />
  106. <div style="margin-left: 10px;">
  107. <span>库存数量:</span>
  108. <el-input
  109. v-model="listQuery.minStock" size="mini" class="filter-item" style="width: 200px;"
  110. maxlength="9"
  111. />
  112. <span> 至 </span>
  113. <el-input
  114. v-model="listQuery.maxStock" size="mini" class="filter-item" style="width: 200px;"
  115. maxlength="9"
  116. />
  117. </div>
  118. <div style="margin-left: 10px;">
  119. <span>价格:</span>
  120. <el-input
  121. v-model="listQuery.minPrice" size="mini" class="filter-item" style="width: 200px;"
  122. maxlength="9"
  123. />
  124. <span> 至 </span>
  125. <el-input
  126. v-model="listQuery.maxPrice" size="mini" class="filter-item" style="width: 200px;"
  127. maxlength="9"
  128. />
  129. </div>
  130. <el-button
  131. size="mini" class="filter-item" type="primary" icon="el-icon-search"
  132. style="margin-left: 10px;"
  133. @click="initList()"
  134. >
  135. 查找
  136. </el-button>
  137. <el-button
  138. size="mini" class="filter-item" type="info" plain
  139. style="margin-left: 10px; padding: 7px 22px; border: 0" @click="clearData"
  140. >
  141. 重置
  142. </el-button>
  143. </div>
  144. <div style="position: relative;padding: 0 0 10px;">
  145. <el-table
  146. ref="multipleTable" :data="productDataList" border
  147. :header-cell-style="{ background: '#EEF3FF', color: '#333333' }" tooltip-effect="dark" style="width: 100%"
  148. @selection-change="(e) => multipleSelection = e"
  149. >
  150. <el-table-column type="selection" width="55" />
  151. <el-table-column label="产品主图" width="220" align="center" prop="image">
  152. <template slot-scope="{ row }">
  153. <el-image
  154. v-if="row.image" lazy :src="common.seamingImgUrl(row.image)" style="width:80px;height:80px;"
  155. fit="cover" :preview-src-list="[ common.seamingImgUrl(row.image) ]"
  156. />
  157. <span v-else>--</span>
  158. </template>
  159. </el-table-column>
  160. <el-table-column prop="productName" label="产品名称" width="220" align="center" />
  161. <el-table-column prop="originalPrice" label="价格(元)" align="center" show-overflow-tooltip />
  162. <el-table-column prop="stockNumber" label="库存(件)" align="center" show-overflow-tooltip />
  163. </el-table>
  164. <div style="position: sticky;bottom: -5px;z-index: 1;padding: 10px;background-color: #eeeeee;">
  165. <el-pagination
  166. :current-page="listQuery.page" :page-sizes="[50, 100, 200, 500, 1000]"
  167. :page-size="listQuery.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="productTotal"
  168. @size-change="(val) => ((listQuery.pageSize = val) && initList())"
  169. @current-change="(val) => ((listQuery.page = val) && initList())"
  170. />
  171. <span>
  172. <el-button type="primary" @click="handleSaveIdList">确 定</el-button>
  173. <el-button @click="isShowManualAdd = false">取 消</el-button>
  174. </span>
  175. </div>
  176. </div>
  177. </el-dialog>
  178. <!-- 关联分组产品 -->
  179. <RelatedGroupProducts ref="RelatedGroupProducts" />
  180. <!-- 智能筛选产品 -->
  181. <IntelProductScreening ref="IntelProductScreening" @success="handleSaveIntelProductList" />
  182. </div>
  183. </el-dialog>
  184. </template>
  185. <script>
  186. import RelatedGroupProducts from './RelatedGroupProducts'
  187. import IntelProductScreening from './IntelProductScreening'
  188. import { commodityListAdd, commodityListUpdate, getGroupList, commodityListGetById, commodityListDelete } from '@/api/commodity'
  189. import XeUtils from 'xe-utils'
  190. export default {
  191. name: 'EditModal',
  192. components: {
  193. RelatedGroupProducts,
  194. IntelProductScreening
  195. },
  196. props: {
  197. groupId: {
  198. type: Number,
  199. default: 0
  200. }
  201. },
  202. data() {
  203. return {
  204. modalOptions: {
  205. closeOnClickModal: false,
  206. width: '1120px',
  207. title: ''
  208. },
  209. visible: false,
  210. formData: {
  211. shopGroupId: '',
  212. groupName: '',
  213. groupImage: '',
  214. groupDescribe: '',
  215. groupLevel: '',
  216. depth: 1,
  217. groupPid: 0,
  218. products: [],
  219. childs: [],
  220. selectType: 1,
  221. ids: [],
  222. condition: '',
  223. conditions: []
  224. },
  225. isShowManualAdd: false, // 手动添加商品模态框
  226. selectedGroupId: '',
  227. productDataList: [],
  228. productTotal: 0,
  229. listQuery: {
  230. maxPrice: null, // 价格最大值
  231. maxStock: null, // 库存数量最大值
  232. minPrice: null, // 价格最小值
  233. minStock: null, // 库存数量最小值
  234. page: 1, // 当前页
  235. pageSize: 50, // 每页记录数
  236. search: '' // 搜索字段
  237. },
  238. multipleSelection: []
  239. }
  240. },
  241. methods: {
  242. handleClose() {
  243. this.visible = false
  244. },
  245. async initList() {
  246. const loading = this.$loading({ text: '加载中' })
  247. const res = await getGroupList(this.listQuery)
  248. this.productTotal = res.data.total
  249. this.productDataList = res.data.list
  250. loading.close()
  251. },
  252. handleOpen(params = {}) {
  253. this.modalOptions.title = params.shopGroupId ? '编辑分组' : '添加分组'
  254. this.visible = true
  255. this.initList()
  256. if (params.shopGroupId) {
  257. this.getInfo(params.shopGroupId)
  258. } else {
  259. this.formData = {
  260. shopGroupId: '',
  261. groupName: '',
  262. groupImage: '',
  263. groupDescribe: '',
  264. groupLevel: '',
  265. depth: 1,
  266. groupPid: 0,
  267. products: [],
  268. childs: [],
  269. selectType: 1, // 自设
  270. ids: [], // 自设。要传。
  271. condition: null, // 自设。要传。1-全部满足 2-任意满足
  272. conditions: [], // 自设。要传。{ calculation: 1-大于 2-等于 3-小于, ids: 已满足部分条件的商品id数组, number: 数值, type: 1-库存 2-价格 3-重量 4-销量 }
  273. orderNumber: '' // 自设
  274. }
  275. }
  276. },
  277. async getInfo(id) {
  278. const loading = this.$loading({ text: '加载中' })
  279. try {
  280. const res = await commodityListGetById({ shopGroupId: id })
  281. this.formData = Object.assign(this.$options.data().formData, res.data, {
  282. shopGroupId: res.data.shopGroupId || '',
  283. groupName: res.data.groupName || '',
  284. groupImage: res.data.groupImage || '',
  285. groupDescribe: res.data.groupDescribe || '',
  286. groupLevel: res.data.groupLevel || '',
  287. depth: res.data.depth || 1,
  288. groupPid: res.data.groupPid || 0,
  289. products: res.data.products || [],
  290. childs: res.data.childs || []
  291. })
  292. XeUtils.eachTree([ this.formData ], (item) => {
  293. item.ids = item.products.map((i) => i.productId)
  294. item.selectType = 1
  295. item.condition = null
  296. item.conditions = []
  297. item.orderNumber = Date.now() - (item.shopGroupId || 0)
  298. }, { children: 'childs' })
  299. } finally {
  300. loading.close()
  301. }
  302. },
  303. handleAppend(data) {
  304. if (data.depth < 3) {
  305. data.childs.push({
  306. shopGroupId: '',
  307. groupName: '',
  308. groupImage: '',
  309. groupDescribe: '',
  310. groupLevel: '',
  311. depth: data.depth + 1,
  312. groupPid: data.shopGroupId,
  313. childs: [],
  314. selectType: 1,
  315. ids: [],
  316. condition: null,
  317. conditions: [],
  318. orderNumber: Date.now()
  319. })
  320. }
  321. },
  322. handleDelete(data, node) {
  323. if (data.shopGroupId) {
  324. let isGroupIdEmpty = false
  325. XeUtils.eachTree([ this.formData ], (item) => {
  326. if (!item.shopGroupId) isGroupIdEmpty = true
  327. }, { children: 'childs' })
  328. if (isGroupIdEmpty) return this.$message({ message: '包含新建分组,请先保存', type: 'warning' })
  329. this.$confirm('选中数据将被永久删除, 是否继续?', '提示', {
  330. confirmButtonText: '确定',
  331. cancelButtonText: '取消',
  332. type: 'warning'
  333. })
  334. .then(async () => {
  335. await commodityListDelete({ shopGroupId: data.shopGroupId })
  336. this.$message({ message: '删除成功!', type: 'success' })
  337. this.getInfo(this.formData.shopGroupId)
  338. this.$emit('success')
  339. })
  340. } else {
  341. XeUtils.eachTree([ this.formData ], (item) => {
  342. if (item.shopGroupId === data.groupPid) {
  343. item.childs.splice(item.childs.findIndex((i) => i.orderNumber === data.orderNumber), 1)
  344. }
  345. }, { children: 'childs' })
  346. }
  347. },
  348. handleAddProduct(data, node) {
  349. this.multipleSelection = []
  350. this.selectedGroupId = data.orderNumber
  351. this.isShowManualAdd = true
  352. this.$nextTick(() => {
  353. this.$refs.multipleTable.clearSelection()
  354. })
  355. },
  356. handleSaveIdList() {
  357. XeUtils.eachTree([ this.formData ], (item) => {
  358. if (item.orderNumber === this.selectedGroupId) {
  359. if (this.multipleSelection.length) {
  360. item.ids = this.multipleSelection.map((i) => i.productId)
  361. } else {
  362. item.ids = []
  363. }
  364. }
  365. }, { children: 'childs' })
  366. this.isShowManualAdd = false
  367. },
  368. handleSaveIntelProductList(e) {
  369. XeUtils.eachTree([ this.formData ], (item) => {
  370. if (item.orderNumber === this.selectedGroupId) {
  371. item.condition = e.condition
  372. item.conditions = e.conditions
  373. }
  374. }, { children: 'childs' })
  375. this.$forceUpdate()
  376. },
  377. clearData() {
  378. this.listQuery = {
  379. maxPrice: null, // 价格最大值
  380. maxStock: null, // 库存数量最大值
  381. minPrice: null, // 价格最小值
  382. minStock: null, // 库存数量最小值
  383. page: 1, // 当前页
  384. pageSize: 10, // 每页记录数
  385. search: '' // 搜索字段
  386. }
  387. this.initList()
  388. },
  389. // 保存提交
  390. async handleSubmit() {
  391. let isGroupNameEmpty = false
  392. XeUtils.eachTree([ this.formData ], (item) => {
  393. if (!item.groupName) isGroupNameEmpty = true
  394. }, { children: 'childs' })
  395. if (isGroupNameEmpty) return this.$message({ message: '分组名称不能为空', type: 'warning' })
  396. let isNoProduct = false
  397. XeUtils.eachTree([ this.formData ], (item) => {
  398. if (item.selectType === 1) {
  399. item.conditions = []
  400. item.condition = null
  401. } else if (item.selectType === 2) {
  402. item.ids = []
  403. } else {
  404. isNoProduct = true
  405. }
  406. }, { children: 'childs' })
  407. if (isNoProduct) return this.$message({ message: '存在分组未选择分组方式', type: 'warning' })
  408. const loading = this.$loading({ text: '提交中,请稍候……' })
  409. try {
  410. const { ...otps } = this.formData
  411. const params = {
  412. ...otps
  413. }
  414. this.formData.shopGroupId ? await commodityListUpdate(params) : await commodityListAdd(params)
  415. loading.close()
  416. this.$message({ message: `${this.formData.shopGroupId ? '编辑' : '添加'}成功!`, type: 'success' })
  417. this.$emit('success')
  418. this.visible = false
  419. } catch (e) {
  420. loading.close()
  421. } finally {
  422. loading.close()
  423. }
  424. }
  425. }
  426. }
  427. </script>
  428. <style lang="scss" scoped>
  429. .tree-container {
  430. ::v-deep .el-tree-node__content {
  431. height: auto;
  432. }
  433. .filter-container {
  434. .filter-item {
  435. display: inline-block;
  436. vertical-align: middle;
  437. margin-bottom: 10px;
  438. }
  439. }
  440. }
  441. .product-dialog {
  442. ::v-deep .el-dialog__body {
  443. padding: 30px 20px 0;
  444. }
  445. }
  446. .add-group-container {
  447. ::v-deep .el-radio-button__inner {
  448. padding: 0;
  449. }
  450. }
  451. </style>