index.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. <template>
  2. <basic-container>
  3. <avue-crud :option="option"
  4. :table-loading="loading"
  5. :data="data"
  6. :page.sync="page"
  7. :permission="permissionList"
  8. :before-open="beforeOpen"
  9. :upload-error="uploadError"
  10. :upload-after="uploadAfter"
  11. v-model="form"
  12. ref="crud"
  13. @row-update="rowUpdate"
  14. @row-click="handleRowClick"
  15. @row-save="rowSave"
  16. @row-del="rowDel"
  17. @search-change="searchChange"
  18. @search-reset="searchReset"
  19. @selection-change="selectionChange"
  20. @current-change="currentChange"
  21. @size-change="sizeChange"
  22. @refresh-change="refreshChange"
  23. @on-load="onLoad">
  24. <template slot="isSale" slot-scope="scope" >
  25. <el-tag v-if="scope.row.$isSale === '上架'" type="success">{{scope.row.$isSale}}</el-tag>
  26. <el-tag v-if="scope.row.$isSale === '下架'" type="danger">{{scope.row.$isSale}}</el-tag>
  27. </template>
  28. <template slot="auditStatus" slot-scope="scope" >
  29. <el-tag v-if="scope.row.auditStatus === 0">{{scope.row.$auditStatus}}</el-tag>
  30. <el-tag v-else-if="scope.row.auditStatus === 1" type="success">{{scope.row.$auditStatus}}</el-tag>
  31. <el-tag v-else-if="scope.row.auditStatus === 2"type="danger">{{scope.row.$auditStatus}}</el-tag>
  32. </template>
  33. <template slot-scope="scope" slot="menu">
  34. <el-button v-if="scope.row.isSale == 1"
  35. type="text"
  36. size="small"
  37. @click="modifyGoodsState(scope.row.id, 0)"
  38. >下架</el-button>
  39. <el-button v-if="scope.row.isSale == 0"
  40. type="text"
  41. size="small"
  42. @click="modifyGoodsState(scope.row.id, 1)"
  43. >上架
  44. </el-button>
  45. <el-button
  46. type="text"
  47. size="small"
  48. @click="copyGoods(scope.row.id)"
  49. >复制</el-button>
  50. </template>
  51. </avue-crud>
  52. <el-dialog title="商品审核" @close="refreshChange"
  53. append-to-body
  54. :visible.sync="auditBox"
  55. :before-close="handleClose"
  56. width="600px"
  57. heigh="40%">
  58. <el-form :model="auditForm" label-width="80px">
  59. <el-form-item label="审核意见:">
  60. <el-select v-model="auditForm.auditStatus" placeholder="请选择">
  61. <el-option
  62. v-for="item in auditList"
  63. :key="item.value"
  64. :label="item.name"
  65. :value="item.value">
  66. </el-option>
  67. </el-select>
  68. </el-form-item>
  69. <el-form-item label="驳回描述:" v-if="auditForm.auditStatus == 2">
  70. <el-input type="textarea" v-model="auditForm.remark" :autosize="{ minRows: 3, maxRows: 5}"></el-input>
  71. </el-form-item>
  72. </el-form>
  73. <div slot="footer" class="dialog-footer">
  74. <el-button @click="handleClose">取 消</el-button>
  75. <el-button type="primary" @click="auditGoods()">确 定</el-button>
  76. </div>
  77. </el-dialog>
  78. </basic-container>
  79. </template>
  80. <script>
  81. import {getList, getDetail, add, update, remove, modifyState, audit, copy} from "@/api/mall/storegoodsinfo";
  82. import {mapGetters} from "vuex";
  83. import {getByParentId} from "../../../api/mall/categoryinfo";
  84. export default {
  85. data() {
  86. var validatePass = (rule, value, callback) => {
  87. if (value>=10 || value <=0) {
  88. callback(new Error('非法的折扣'));
  89. } else {
  90. callback();
  91. }
  92. }
  93. return {
  94. option: {
  95. height: 'auto',
  96. calcHeight: 30,
  97. border: true,
  98. index: false,
  99. viewBtn: true,
  100. delBtn:false,
  101. editBtn: true,
  102. // selection: true,
  103. dialogClickModal: false,
  104. labelWidth: 150,
  105. searchIcon: true,
  106. searchIndex: 3,
  107. column: [
  108. {
  109. label: "商品编号",
  110. search: true,
  111. prop: "goodsNo",
  112. overHidden: true,
  113. addDisplay: false,
  114. editDisplay: false,
  115. editDisabled: true,
  116. width: 160
  117. },
  118. {
  119. label: "缩略图",
  120. width: 80,
  121. prop: "goodsCover",
  122. addDisplay: false,
  123. viewDisplay: false,
  124. editDisplay: false,
  125. type: 'img',
  126. },
  127. {
  128. label: "商品名称",
  129. prop: "goodsName",
  130. search: true,
  131. width: 280,
  132. overHidden: true,
  133. rules: [{
  134. required: true,
  135. message: "请输入商品名称",
  136. trigger: "blur"
  137. }]
  138. },
  139. {
  140. label: "规格",
  141. prop: "goodsSpec",
  142. rules: [{
  143. required: true,
  144. message: "请输入规格",
  145. trigger: "blur"
  146. }]
  147. },
  148. {
  149. label: "品牌",
  150. prop: "brandDesc",
  151. hide: true,
  152. addDisplay: false,
  153. viewDisplay: false,
  154. editDisplay: false,
  155. },
  156. {
  157. label: "分类",
  158. prop: "categoryIdArr",
  159. hide: true,
  160. search: true,
  161. lazy: true,
  162. rules: [{
  163. required: true,
  164. message: "请选择分类",
  165. trigger: "blur"
  166. }],
  167. type: "cascader",
  168. filterable: true,
  169. checkStrictly: true,
  170. lazyLoad(node, resolve) {
  171. let parentId;
  172. let stop_level = 2;
  173. let level = node.level;
  174. let list = [];
  175. if (node.level === 0) {
  176. parentId = 0;
  177. getByParentId(0).then((res) => {
  178. list = res.data.data;
  179. list = (list || []).map(ele => {
  180. return Object.assign(ele, {
  181. leaf: level >= stop_level
  182. })
  183. })
  184. resolve(list)
  185. })
  186. } else {
  187. parentId = node.data.id;
  188. getByParentId(node.data.id).then((res) => {
  189. list = res.data.data;
  190. list = (list || []).map(ele => {
  191. return Object.assign(ele, {
  192. leaf: level >= stop_level
  193. })
  194. })
  195. resolve(list)
  196. })
  197. }
  198. },
  199. props: {
  200. label: "title",
  201. value: "key"
  202. },
  203. },
  204. {
  205. label: "品牌",
  206. prop: "brandId",
  207. hide: true,
  208. search: true,
  209. type: "select",
  210. remote: true,
  211. dicUrl: "/api/mall/brandsinfo/getByName?brandName={{key}}",
  212. props: {
  213. label: "brandName",
  214. value: "id"
  215. },
  216. dataType: "string",
  217. },
  218. // {
  219. // label: "折扣",
  220. // prop: "discount",
  221. // hide: true,
  222. // precision: 2,
  223. // type: "number",
  224. // rules: [{
  225. // required: true,
  226. // message: "请输入折扣",
  227. // trigger: "blur"
  228. // },{ validator: validatePass, trigger: 'blur' }],
  229. // },
  230. {
  231. label: "供货价",
  232. prop: "costPrice",
  233. precision: 2,
  234. type: "number",
  235. formatter:(val,value)=>{
  236. return Number(value).toFixed(2);
  237. },
  238. rules: [{
  239. required: true,
  240. message: "请输入供货价",
  241. trigger: "blur"
  242. }],
  243. },
  244. {
  245. label: "划线价",
  246. prop: "salePrice",
  247. precision: 2,
  248. type: "number",
  249. formatter:(val,value)=>{
  250. return Number(value).toFixed(2);
  251. },
  252. rules: [{
  253. required: true,
  254. message: "请输入划线价",
  255. trigger: "blur"
  256. }],
  257. },
  258. {
  259. label: "状态",
  260. prop: "isSale",
  261. search: true,
  262. slot:true,
  263. rules: [{
  264. required: true,
  265. message: "请输入是否上架",
  266. trigger: "blur"
  267. }],
  268. type: "select",
  269. dicUrl: "/api/blade-system/dict-biz/getEnumDict?enumName=GoodsStateEnum",
  270. props: {
  271. label: "name",
  272. value: "value"
  273. },
  274. dataType: "number",
  275. },
  276. {
  277. label: "上架时间",
  278. prop: "saleTime",
  279. hide: true,
  280. addDisplay: false,
  281. viewDisplay: false,
  282. editDisplay: false,
  283. editDisabled: true,
  284. },
  285. {
  286. label: "总库存",
  287. prop: "totalStock",
  288. type: "number",
  289. hide: true,
  290. min: 1,
  291. rules: [
  292. {required: true, message: "请输入总库存", trigger: "blur"},
  293. ]
  294. },
  295. {
  296. label: "总销量",
  297. prop: "totalSales",
  298. hide: true,
  299. addDisplay: false,
  300. editDisplay: false,
  301. },
  302. {
  303. label: "总浏览量",
  304. prop: "totalViews",
  305. hide: true,
  306. addDisplay: false,
  307. editDisplay: false,
  308. },
  309. {
  310. label: "单位",
  311. prop: "unit",
  312. hide: true,
  313. rules: [{
  314. required: true,
  315. message: "请选择单位",
  316. trigger: "blur"
  317. }],
  318. type: "select",
  319. remote: true,
  320. dicUrl: "/api/mall/unit/all-list",
  321. props: {
  322. label: "name",
  323. value: "id"
  324. },
  325. dataType: "string",
  326. },
  327. {
  328. label: "产地",
  329. prop: "origin",
  330. hide: true,
  331. },
  332. {
  333. label: "创建时间",
  334. prop: "createTime",
  335. hide: true,
  336. addDisplay: false,
  337. editDisplay: false,
  338. editDisabled: false,
  339. },
  340. {
  341. label: '商品缩略图',
  342. prop: 'uploadCover',
  343. type: 'upload',
  344. listType: 'picture-img',
  345. loadText: '上传中,请稍等',
  346. accept: 'image/png, image/jpeg',
  347. fileSize: 500,
  348. tip: '只能上传jpg/png文件,且不超过500kb',
  349. span: 24,
  350. hide: true,
  351. propsHttp: {
  352. res: 'data',
  353. url: 'link',
  354. },
  355. action: "/api/blade-resource/oss/endpoint/put-file-attach"
  356. },
  357. {
  358. label: '商品主图',
  359. prop: 'slideshowList',
  360. type: 'upload',
  361. dataType: 'array',
  362. listType: 'picture-card',
  363. loadText: '上传中,请稍等',
  364. accept: 'image/png, image/jpeg',
  365. fileSize: 1024,
  366. tip: '只能上传jpg/png文件,且不超过1M',
  367. span: 24,
  368. hide: true,
  369. propsHttp: {
  370. res: 'data',
  371. url: 'link',
  372. },
  373. action: "/api/blade-resource/oss/endpoint/put-file-attach"
  374. },
  375. // {
  376. // label: '图文详情',
  377. // prop: 'detailImgUrlList',
  378. // type: 'upload',
  379. // dataType: 'array',
  380. // listType: 'picture-card',
  381. // loadText: '上传中,请稍等',
  382. // accept: 'image/png, image/jpeg',
  383. // fileSize: 2048,
  384. // tip: '只能上传jpg/png文件,且不超过2M',
  385. // limit: 25,
  386. // span: 24,
  387. // hide: true,
  388. // propsHttp: {
  389. // res: 'data',
  390. // url: 'link',
  391. // },
  392. // action: "/api/blade-resource/oss/endpoint/put-file-attach"
  393. // },
  394. {
  395. label: "图文详情",
  396. prop: "detailContent",
  397. hide: true,
  398. span: 24,
  399. component: 'AvueUeditor',
  400. options: {
  401. action: '/api/blade-resource/oss/endpoint/put-file',
  402. props: {
  403. res: "data",
  404. url: "link",
  405. }
  406. },
  407. },
  408. ]
  409. },
  410. range: {
  411. from: '',
  412. to: ''
  413. },
  414. categoryData: [],
  415. props: {
  416. label: "title",
  417. value: "key"
  418. },
  419. categoryIdArr: [],
  420. auditBox: false,
  421. auditForm: {
  422. auditStatus: '',
  423. id: '',
  424. remark: ''
  425. },
  426. form: {
  427. },
  428. goodsParamForm: {},
  429. goodsSpecForm: {},
  430. scForm: {},
  431. query: {},
  432. goodsParamQuery: {},
  433. viewSpecQuery: {},
  434. loading: true,
  435. paramLoading: true,
  436. box: false,
  437. page: {
  438. pageSize: 10,
  439. currentPage: 1,
  440. total: 0
  441. },
  442. data: [],
  443. goodsParamData: [],
  444. viewSpecData: [],
  445. scData: [],
  446. viewDialog: false,
  447. auditList: [
  448. {name: '通过', value: 1},{name: '驳回', value: 2}
  449. ]
  450. };
  451. },
  452. computed: {
  453. ...mapGetters(["permission"]),
  454. permissionList() {
  455. return {
  456. // addBtn: this.vaildData(this.permission.goodsinfo_add, false),
  457. // viewBtn: this.vaildData(this.permission.goodsinfo_view, false),
  458. // delBtn: this.vaildData(this.permission.goodsinfo_delete, false),
  459. // editBtn: this.vaildData(this.permission.goodsinfo_edit, false)
  460. };
  461. },
  462. ids() {
  463. let ids = [];
  464. this.selectionList.forEach(ele => {
  465. ids.push(ele.id);
  466. });
  467. return ids.join(",");
  468. },
  469. specIds() {
  470. let ids = [];
  471. this.specSelectionList.forEach(ele => {
  472. ids.push(ele.id);
  473. });
  474. return ids.join(",");
  475. }
  476. },
  477. methods: {
  478. viewDialogShow(row) {
  479. this.viewDialog = true;
  480. this.demoModelSrc = `${this.demoModelSrcYuan}?aa=${JSON.stringify(row)}`
  481. },
  482. rowSave(row, done, loading) {
  483. row.goodsCategoryId = row.categoryIdArr.join("-");
  484. if (row.promotionRateRange == '') {
  485. delete row.promotionRateRange;
  486. }
  487. add(row).then(() => {
  488. this.onLoad(this.page);
  489. this.$message({
  490. type: "success",
  491. message: "操作成功!"
  492. });
  493. done();
  494. }, error => {
  495. loading();
  496. window.console.log(error);
  497. });
  498. },
  499. rowUpdate(row, index, done, loading) {
  500. row.goodsCategoryId = row.categoryIdArr.join("-") + "-";
  501. if (row.promotionRateRange == '') {
  502. delete row.promotionRateRange;
  503. }
  504. update(row).then(() => {
  505. this.onLoad(this.page);
  506. this.$message({
  507. type: "success",
  508. message: "操作成功!"
  509. });
  510. done();
  511. }, error => {
  512. loading();
  513. console.log(error);
  514. });
  515. },
  516. rowDel(row) {
  517. this.$confirm("确定将选择数据删除?", {
  518. confirmButtonText: "确定",
  519. cancelButtonText: "取消",
  520. type: "warning"
  521. })
  522. .then(() => {
  523. return remove(row.id);
  524. })
  525. .then(() => {
  526. this.onLoad(this.page);
  527. this.$message({
  528. type: "success",
  529. message: "操作成功!"
  530. });
  531. });
  532. },
  533. handleDelete() {
  534. if (this.selectionList.length === 0) {
  535. this.$message.warning("请选择至少一条数据");
  536. return;
  537. }
  538. this.$confirm("确定将选择数据删除?", {
  539. confirmButtonText: "确定",
  540. cancelButtonText: "取消",
  541. type: "warning"
  542. })
  543. .then(() => {
  544. return remove(this.ids);
  545. })
  546. .then(() => {
  547. this.onLoad(this.page);
  548. this.$message({
  549. type: "success",
  550. message: "操作成功!"
  551. });
  552. this.$refs.crud.toggleSelection();
  553. });
  554. },
  555. beforeOpen(done, type) {
  556. if (["edit", "view"].includes(type)) {
  557. getDetail(this.form.id).then(res => {
  558. this.form = res.data.data;
  559. this.form.uploadCover = res.data.data.goodsCover;
  560. this.form.goodsVideo = res.data.data.videoUrl;
  561. this.form.slideshowList = res.data.data.slideshowList;
  562. this.form.detailImgUrlList = res.data.data.detailImgUrlList;
  563. this.form.categoryIdArr = this.form.goodsCategoryId.split("-");
  564. });
  565. console.log(this.form.goodsCategoryId.split("-"));
  566. }
  567. done();
  568. },
  569. searchReset() {
  570. this.query = {};
  571. this.range = {
  572. from: '',
  573. to: ''
  574. };
  575. this.activityDate = '';
  576. this.onLoad(this.page);
  577. },
  578. searchChange(params, done) {
  579. this.query = params;
  580. this.page.currentPage = 1;
  581. this.onLoad(this.page, params);
  582. done();
  583. },
  584. selectionChange(list) {
  585. this.selectionList = list;
  586. },
  587. selectionClear() {
  588. this.selectionList = [];
  589. this.$refs.crud.toggleSelection();
  590. },
  591. currentChange(currentPage){
  592. this.page.currentPage = currentPage;
  593. },
  594. sizeChange(pageSize){
  595. this.page.pageSize = pageSize;
  596. },
  597. refreshChange() {
  598. this.onLoad(this.page, this.query);
  599. },
  600. onLoad(page, params = {}) {
  601. this.loading = true;
  602. if (this.query.categoryIdArr) {
  603. this.query.goodsCategoryId = this.query.categoryIdArr.join("-");
  604. this.query.categoryIdArr = null;
  605. }
  606. this.query.activityDate = this.activityDate;
  607. getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
  608. const data = res.data.data;
  609. this.page.total = data.total;
  610. this.data = data.records;
  611. this.loading = false;
  612. this.selectionClear();
  613. });
  614. },
  615. uploadError(error, column) {
  616. this.$message.success('上传失败:' + error)
  617. console.log(error, column)
  618. },
  619. uploadAfter(res, done, loading,column) {
  620. console.log(res,column)
  621. if(column.prop == "uploadCover") {
  622. this.form.goodsCover = res.link;
  623. }
  624. if (column.prop == "goodsVideo") {
  625. this.form.videoUrl = res.link;
  626. }
  627. done()
  628. },
  629. paramConfig(row) {
  630. this.box = true;
  631. this.goodsParamQuery.goodsId = row.id;
  632. this.onLoadParam();
  633. },
  634. handleRowClick(row, event, column, loading) {
  635. if (event.label == '是否上架') {
  636. const param = {id:row.id, isSale:row.saleSwitch ? 1 : 0}
  637. update(param).then(() => {
  638. this.onLoad(this.page);
  639. this.$message({
  640. type: "success",
  641. message: row.saleSwitch ? "上架成功" : "下架成功"
  642. });
  643. }, error => {
  644. loading();
  645. console.log(error);
  646. });
  647. }
  648. },
  649. handleClose() {
  650. this.categoryIdArr = [];
  651. this.auditBox = false;
  652. this.auditForm = {
  653. auditStatus: '',
  654. id: '',
  655. remark: ''
  656. };
  657. },
  658. modifyGoodsStateBatch(state) {
  659. if (this.selectionList.length === 0) {
  660. this.$message.warning("请选择至少一条数据");
  661. return;
  662. }
  663. this.$confirm("确定修改数据?", {
  664. confirmButtonText: "确定",
  665. cancelButtonText: "取消",
  666. type: "warning"
  667. })
  668. .then(() => {
  669. const loading = this.$loading({
  670. lock: true,
  671. text: '修改中,请稍等...',
  672. spinner: 'el-icon-loading',
  673. background: 'rgba(0, 0, 0, 0.7)'
  674. });
  675. modifyState(this.ids, state).then(() => {
  676. loading.close();
  677. this.onLoad(this.page, this.query);
  678. this.$message({
  679. type: "success",
  680. message: "操作成功!"
  681. });
  682. this.selectionClear()
  683. });
  684. })
  685. },
  686. modifyGoodsState(id, state) {
  687. let title = state === 0 ? '确定下架?' : '确定上架?';
  688. this.$confirm(title, {
  689. confirmButtonText: "确定",
  690. cancelButtonText: "取消",
  691. type: "warning"
  692. }).then(() => {
  693. const loading = this.$loading({
  694. lock: true,
  695. text: '修改中,请稍等...',
  696. spinner: 'el-icon-loading',
  697. background: 'rgba(0, 0, 0, 0.7)'
  698. });
  699. modifyState(id, state).then(() => {
  700. loading.close();
  701. this.onLoad(this.page, this.query);
  702. this.$message({
  703. type: "success",
  704. message: "操作成功!"
  705. });
  706. this.selectionClear()
  707. });
  708. })
  709. },
  710. openAuditBox(id) {
  711. this.auditBox = true;
  712. this.auditForm.id = id;
  713. },
  714. auditGoods() {
  715. audit(this.auditForm).then(() => {
  716. this.auditBox = false;
  717. this.onLoad(this.page, this.query);
  718. this.$message({
  719. type: "success",
  720. message: "操作成功!"
  721. });
  722. }, error => {
  723. loading();
  724. console.log(error);
  725. });
  726. },
  727. copyGoods(goodsId) {
  728. copy(goodsId).then(res => {
  729. this.$message({
  730. type: "success",
  731. message: "复制成功"
  732. });
  733. this.onLoad(this.page);
  734. })
  735. }
  736. }
  737. };
  738. </script>
  739. <style>
  740. </style>