goodsList.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. <template>
  2. <div class="app-container calendar-list-container">
  3. <!-- 查询和其他操作 -->
  4. <div class="filter-container">
  5. <el-input clearable class="filter-item" style="width: 200px;" placeholder="商品名称"
  6. v-model="listQuery.name"></el-input>
  7. <el-input clearable class="filter-item" style="width: 200px;" placeholder="商品编号"
  8. v-model="listQuery.seq"></el-input>
  9. <!-- <el-date-picker class="filter-item" v-model="listQuery.shelfTime" type="datetime" placeholder="商品上架时间">
  10. </el-date-picker> -->
  11. <el-select v-model="listQuery.status" clearable placeholder="状态" style="top: -4px;width: 200px;">
  12. <el-option :key="item.type" v-for="item in goodsStatusList" :label="item.name" :value="item.type">
  13. </el-option>
  14. </el-select>
  15. <el-button class="filter-item" type="primary" v-waves icon="el-icon-search" @click="handleFilter">查找</el-button>
  16. <el-button class="filter-item" type="primary" @click="handleCreate" icon="el-icon-edit">添加</el-button>
  17. </div>
  18. <!-- 查询结果 -->
  19. <el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit
  20. highlight-current-row>
  21. <el-table-column type="index" label="序号" header-align="center" align="center">
  22. </el-table-column>
  23. <el-table-column align="center" min-width="200px" label="商品名称" prop="name">
  24. </el-table-column>
  25. <el-table-column align="center" min-width="120px" label="商品编号" prop="seq">
  26. </el-table-column>
  27. <el-table-column align="center" min-width="80px" label="商品属性">
  28. <template slot-scope="props">
  29. <span v-if="props.row.productAttribute == 1">实物</span>
  30. <span v-if="props.row.productAttribute == 0">虚拟</span>
  31. </template>
  32. </el-table-column>
  33. <el-table-column align="center" min-width="80px" label="积分" prop="price">
  34. </el-table-column>
  35. <el-table-column align="center" min-width="100px" label="库存量" prop="actualStock">
  36. </el-table-column>
  37. <el-table-column align="center" min-width="100px" label="可兑换库存量" prop="stock">
  38. </el-table-column>
  39. <el-table-column align="center" min-width="80px" label="兑换方式">
  40. <template slot-scope="props">
  41. <span v-if="props.row.deliveryType == 1">快递</span>
  42. <span v-if="props.row.deliveryType == 0">无需快递</span>
  43. </template>
  44. </el-table-column>
  45. <el-table-column align="center" min-width="100px" label="商品所在地" prop="location">
  46. </el-table-column>
  47. <el-table-column align="center" min-width="80px" label="商品状态">
  48. <template slot-scope="props">
  49. <span v-if="props.row.status == 1">上架</span>
  50. <span v-if="props.row.status == 0">下架</span>
  51. </template>
  52. </el-table-column>
  53. <el-table-column align="center" min-width="80px" label="兑换记录">
  54. <template slot-scope="props">
  55. <span @click="handleView(props.row)" style="color:green;cursor: pointer;">查看</span>
  56. </template>
  57. </el-table-column>
  58. <!-- fixed="right" -->
  59. <el-table-column align="center" label="操作" width="240px" class-name="small-padding fixed-width">
  60. <template slot-scope="scope">
  61. <el-button type="primary" size="small" @click="handleUpdate(scope.row)">编辑</el-button>
  62. <el-button v-if="scope.row.status == 0" type="success" size="small"
  63. @click="changeGoodsState(scope.row.skuId, 1)">上架</el-button>
  64. <el-button v-if="scope.row.status == 1" type="warning" size="small"
  65. @click="changeGoodsState(scope.row.skuId, 0)">下架</el-button>
  66. <el-button type="danger" size="mini" @click="handleDelete(scope.row.skuId, -1)">删除</el-button>
  67. </template>
  68. </el-table-column>
  69. </el-table>
  70. <!-- 分页 -->
  71. <div class="pagination-container">
  72. <el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange"
  73. :current-page="listQuery.page" :page-sizes="[10, 20, 30, 50]" :page-size="listQuery.limit"
  74. layout="total, sizes, prev, pager, next, jumper" :total="total">
  75. </el-pagination>
  76. </div>
  77. <!-- 员工积分记录列表 -->
  78. <el-dialog title="兑换记录" :visible.sync="dialogListVisible" width="70%" style="overflow: auto;">
  79. <div class="filter-container">
  80. <el-select filterable v-model="itemListQuery.deptId" clearable placeholder="部门" style="top: -4px;width: 200px;">
  81. <el-option :key="item.deptId" v-for="item in depTypeList" :label="item.deptName" :value="item.deptId">
  82. </el-option>
  83. </el-select>
  84. <el-input clearable class="filter-item" style="width: 200px;" placeholder="员工名称"
  85. v-model="itemListQuery.userName"></el-input>
  86. <el-button class="filter-item" type="primary" v-waves icon="el-icon-search"
  87. @click="itemHandleFilter">查找</el-button>
  88. <el-button class="filter-item" type="primary" @click="handleDownLoad" v-waves icon="el-icon-download">导出</el-button>
  89. </div>
  90. <!-- 查询结果 -->
  91. <el-table size="small" :data="itemList" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit
  92. highlight-current-row>
  93. <el-table-column type="index" label="序号" header-align="center" align="center">
  94. </el-table-column>
  95. <el-table-column align="center" width="200px" label="部门" prop="deptName">
  96. </el-table-column>
  97. <el-table-column align="center" label="员工姓名" prop="purchaser">
  98. </el-table-column>
  99. <el-table-column align="center" label="兑换方式" prop="deliveryTypeName">
  100. </el-table-column>
  101. <el-table-column align="center" label="联系人" prop="contact">
  102. </el-table-column>
  103. <el-table-column align="center" width="120px" label="联系方式" prop="contactPhone">
  104. </el-table-column>
  105. <el-table-column align="center" width="200px" label="地址" prop="contactAddr">
  106. </el-table-column>
  107. <el-table-column align="center" width="200px" label="邮箱" prop="contactEmail">
  108. </el-table-column>
  109. <el-table-column align="center" width="150px" label="兑换时间" prop="createTime">
  110. </el-table-column>
  111. </el-table>
  112. <!-- 分页 -->
  113. <div class="pagination-container">
  114. <el-pagination background @size-change="itemHandleSizeChange" @current-change="itemHandleCurrentChange"
  115. :current-page="itemListQuery.page" :page-sizes="[10, 20, 30, 50]" :page-size="itemListQuery.limit"
  116. layout="total, sizes, prev, pager, next, jumper" :total="itemTotal">
  117. </el-pagination>
  118. </div>
  119. </el-dialog>
  120. <!-- 添加或修改对话框 -->
  121. <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="70%">
  122. <el-form :rules="rules" ref="dataForm" :model="dataForm" status-icon label-position="left" label-width="100px" style='width:700px; margin-left:50px;'>
  123. <el-form-item label="商品编号" prop="seq">
  124. <el-input disabled placeholder="添加成功后自动生成" readonly v-model="dataForm.seq"></el-input>
  125. </el-form-item>
  126. <el-form-item label="商品名称" prop="name">
  127. <el-input v-model="dataForm.name"></el-input>
  128. </el-form-item>
  129. <el-form-item label="商品属性" prop="productAttribute">
  130. <el-select v-model="dataForm.productAttribute" filterable placeholder="请选择" style="width: 350px"
  131. value-key="value" @change="changeProductAttribute">
  132. <el-option :key="item.dictValue" v-for="item in productAttributeList" :label="item.dictLabel"
  133. :value="item.dictValue">
  134. </el-option>
  135. </el-select>
  136. </el-form-item>
  137. <el-form-item label="兑换方式" prop="deliveryType">
  138. <el-select v-model="dataForm.deliveryType" filterable placeholder="请选择" style="width: 350px">
  139. <el-option :key="item.type" v-for="item in deliveryTypeList" :label="item.name" :value="item.type">
  140. </el-option>
  141. </el-select>
  142. </el-form-item>
  143. <el-form-item v-if="dataForm.productAttribute==1" label="商品所在地" prop="location">
  144. <el-select v-model="dataForm.location" filterable placeholder="请选择" style="width: 350px">
  145. <el-option :key="item.dictValue" v-for="item in locationList" :label="item.dictLabel" :value="item.dictValue">
  146. </el-option>
  147. </el-select>
  148. </el-form-item>
  149. <el-form-item style="width: 800px" label="商品图片" prop="imgUrl">
  150. <el-tooltip content="建议图片宽高比260*200" placement="top-start">
  151. <el-upload :limit="1" :action="fileImgUrl" list-type="picture-card" :file-list="dataForm.images"
  152. :on-success="handleGallerySucess" :on-exceed="handleExceed" :before-upload="uploadBannerImg"
  153. :on-remove="handleRemove">
  154. <i class="el-icon-plus"></i>
  155. </el-upload>
  156. </el-tooltip>
  157. </el-form-item>
  158. <el-form-item label="积分" prop="price">
  159. <el-input v-model="dataForm.price"></el-input>
  160. </el-form-item>
  161. <el-form-item label="库存量" prop="actualStock">
  162. <el-input-number :precision="0" :step="1" v-model="dataForm.actualStock"></el-input-number>
  163. </el-form-item>
  164. <el-form-item
  165. style="width: 800px"
  166. label="商品说明"
  167. prop="content"
  168. >
  169. <tinymce v-model="dataForm.content" ref="tinymce"></tinymce>
  170. </el-form-item>
  171. </el-form>
  172. <div slot="footer" class="dialog-footer">
  173. <el-button @click="dialogFormVisible = false">取消</el-button>
  174. <el-button v-if="dialogStatus == 'create'" type="primary" @click="createData">确定</el-button>
  175. <el-button v-else type="primary" @click="updateData">确定</el-button>
  176. </div>
  177. </el-dialog>
  178. </div>
  179. </template>
  180. <style>
  181. .demo-table-expand {
  182. font-size: 0;
  183. }
  184. .demo-table-expand label {
  185. width: 200px;
  186. color: #99a9bf;
  187. }
  188. .demo-table-expand .el-form-item {
  189. margin-right: 0;
  190. margin-bottom: 0;
  191. }
  192. </style>
  193. <script>
  194. import { createItem, updateItem, goodsList, goodsState, exchangeHistory } from "@/api/goodsManage";
  195. import { depTypeList,dataTypeList } from "@/api/public";
  196. import waves from "@/directive/waves"; // 水波纹指令
  197. import Tinymce from '@/components/Tinymce'
  198. export default {
  199. name: 'goodsList',
  200. components: { Tinymce },
  201. directives: { waves },
  202. data() {
  203. return {
  204. locationList:['洛阳','北京','上海'],
  205. productAttributeList: [
  206. {
  207. type: 1,
  208. name: '实物'
  209. },
  210. {
  211. type: 0,
  212. name: '虚拟'
  213. },
  214. ],
  215. deliveryTypeList: [
  216. {
  217. type: 1,
  218. name: '快递'
  219. },
  220. {
  221. type: 0,
  222. name: '无需快递'
  223. },
  224. ],
  225. goodsStatusList: [
  226. {
  227. type: 1,
  228. name: '上架'
  229. },
  230. {
  231. type: 0,
  232. name: '下架'
  233. },
  234. ],
  235. depTypeList: [],
  236. list: [],
  237. itemList: [],
  238. total: 0,
  239. itemTotal: 0,
  240. listLoading: false,
  241. downloadLoading: false,
  242. listQuery: {
  243. page: 1,
  244. limit: 10,
  245. name: '',
  246. seq: '',
  247. status: '',
  248. },
  249. itemListQuery: {
  250. page: 1,
  251. limit: 10,
  252. skuId:'',
  253. deptId: '',
  254. userName: '',
  255. },
  256. dataForm: {
  257. name: undefined,
  258. seq: undefined,
  259. productAttribute: undefined,
  260. imgUrl: undefined,
  261. price: undefined,
  262. actualStock: undefined,
  263. deliveryType: undefined,
  264. location: undefined,
  265. images: [],
  266. },
  267. dialogFormVisible: false,
  268. dialogStatus: '',
  269. textMap: {
  270. update: "编辑",
  271. create: "创建",
  272. },
  273. rules: {
  274. name: [{ required: true, message: "请填写商品名称", trigger: "blur" }],
  275. imgUrl: [{ required: true, message: "请上传商品图片", trigger: "blur" }],
  276. productAttribute: [{ required: true, message: "请选择商品属性", trigger: "blur" }],
  277. price: [{ required: true, message: "请填写商品积分", trigger: "blur" }],
  278. actualStock: [{ required: true, message: "请设置库存量", trigger: "blur" }],
  279. deliveryType: [{ required: true, message: "请选择兑换方式", trigger: "blur" }],
  280. location: [{ required: true, message: "请选择商品所在地", trigger: "blur" }],
  281. content: [{ required: true, message: "说明不能为空", trigger: "blur" }],
  282. },
  283. dialogListVisible: false,
  284. fileImgUrl: this.upLoadUrl,
  285. }
  286. },
  287. created() {
  288. this.getProductAttributeList();
  289. this.getDataTypeList();
  290. this.getDepTypeList();
  291. this.getList();
  292. },
  293. methods: {
  294. handleDownLoad(){
  295. window.location.href = process.env.BASE_API + '/mall-sku/exportSkuLog?skuId=' + this.itemListQuery.skuId + '&deptId=' + this.itemListQuery.deptId + '&userName=' + this.itemListQuery.userName;
  296. },
  297. changeProductAttribute(val) {
  298. this.dataForm.deliveryType = '';
  299. this.dataForm.location = '';
  300. if (val == 1) {
  301. this.deliveryTypeList = [
  302. {
  303. type: 1,
  304. name: '快递'
  305. },
  306. {
  307. type: 0,
  308. name: '无需快递'
  309. },
  310. ]
  311. } else {
  312. this.deliveryTypeList = [
  313. {
  314. type: 0,
  315. name: '无需快递'
  316. },
  317. ]
  318. }
  319. },
  320. handleRemove(file, fileList) {
  321. console.log(file, fileList);
  322. let images = [];
  323. for (let i in fileList) {
  324. let response = fileList[i].response;
  325. let url = response.data.url;
  326. images.push(url);
  327. this.dataForm.imgUrl = images.join(",");
  328. }
  329. },
  330. uploadBannerImg(file) {
  331. const isJPGs = file.type === "image/jpeg";
  332. console.log(isJPGs);
  333. },
  334. handleExceed(files, fileList) {
  335. this.$message.warning(
  336. `当前限制选择 1 个文件,本次选择了 ${files.length} 个文件!,共选择了 ${files.length + fileList.length
  337. } 个文件`
  338. );
  339. },
  340. handleGallerySucess(res, file, fileList) {
  341. this.dataForm.imgUrl = ""; // 清空画廊图片数组
  342. let images = [];
  343. for (let i in fileList) {
  344. let response = fileList[i].response;
  345. if (response.errno && response.errno != "0") {
  346. this.$message.error("该图片上传失败,已被移除,请重新上传!");
  347. // 上传失败移除该 file 对象
  348. fileList.splice(i, 1);
  349. } else {
  350. let url = response.data.url;
  351. images.push(url);
  352. }
  353. }
  354. this.dataForm.imgUrl = images.join(",");
  355. },
  356. resetForm() {
  357. this.dataForm = {
  358. content: undefined,
  359. name: undefined,
  360. seq: undefined,
  361. productAttribute: undefined,
  362. imgUrl: undefined,
  363. price: undefined,
  364. actualStock: undefined,
  365. deliveryType: undefined,
  366. location: undefined,
  367. images: [],
  368. };
  369. },
  370. handleCreate() {
  371. this.resetForm();
  372. this.dialogFormVisible = true;
  373. this.dialogStatus = "create";
  374. this.$nextTick(() => {
  375. this.$refs.tinymce.setContent("");
  376. this.$refs["dataForm"].clearValidate();
  377. });
  378. },
  379. createData() {
  380. this.$refs["dataForm"].validate((valid) => {
  381. if (valid) {
  382. console.log(this.dataForm);
  383. createItem(this.dataForm)
  384. .then((response) => {
  385. this.getList();
  386. this.dialogFormVisible = false;
  387. this.$notify({
  388. title: "成功",
  389. message: "创建成功",
  390. type: "success",
  391. duration: 2000,
  392. });
  393. this.reload();
  394. })
  395. .catch(() => { });
  396. }
  397. });
  398. },
  399. handleUpdate(row) {
  400. this.dataForm = Object.assign({}, row);
  401. let content = this.dataForm.content;
  402. if (row.productAttribute == 1) {
  403. this.deliveryTypeList = [
  404. {
  405. type: 1,
  406. name: '快递'
  407. },
  408. {
  409. type: 0,
  410. name: '无需快递'
  411. },
  412. ]
  413. } else {
  414. this.deliveryTypeList = [
  415. {
  416. type: 0,
  417. name: '无需快递'
  418. },
  419. ]
  420. }
  421. if (this.dataForm.imgUrl) {
  422. let images = this.dataForm.imgUrl.split(",");
  423. this.dataForm.images = [];
  424. for (let i in images) {
  425. let url = images[i];
  426. let name = "image_" + i;
  427. this.dataForm.images.push({
  428. name: name,
  429. url: url,
  430. response: { error: "0", data: { url: url } },
  431. });
  432. }
  433. }
  434. this.dialogStatus = 'update'
  435. this.dialogFormVisible = true
  436. this.$nextTick(() => {
  437. this.$refs.tinymce.setContent(content);
  438. this.$refs['dataForm'].clearValidate()
  439. })
  440. },
  441. updateData() {
  442. this.$refs['dataForm'].validate((valid) => {
  443. if (valid) {
  444. updateItem(this.dataForm).then(() => {
  445. this.dialogFormVisible = false
  446. this.$notify({
  447. title: '成功',
  448. message: '更新成功',
  449. type: 'success',
  450. duration: 2000
  451. })
  452. this.getList()
  453. })
  454. }
  455. })
  456. },
  457. changeGoodsState(id, index) {
  458. goodsState({ skuId: id, status: index }).then(response => {
  459. this.$notify({
  460. title: '成功',
  461. message: '商品状态修改成功',
  462. type: 'success',
  463. duration: 2000
  464. })
  465. this.getList()
  466. })
  467. },
  468. handleDelete(id, index) {
  469. this.$confirm('确认删除吗?', '提示', {
  470. confirmButtonText: '确定',
  471. cancelButtonText: '取消',
  472. type: 'warning'
  473. }).then(() => {
  474. goodsState({ skuId: id, status: index }).then(response => {
  475. this.$notify({
  476. title: '成功',
  477. message: '删除成功',
  478. type: 'success',
  479. duration: 2000
  480. })
  481. this.getList();
  482. })
  483. }).catch(() => {
  484. })
  485. },
  486. getDataTypeList() {
  487. dataTypeList({dictType:'mall_sku_location'}).then(response => {
  488. this.locationList = response.data.data;
  489. }).catch(() => {});
  490. },
  491. getProductAttributeList(){
  492. dataTypeList({dictType:'mall_sku_attribute'}).then(response => {
  493. this.productAttributeList = response.data.data;
  494. }).catch(() => {});
  495. },
  496. getDepTypeList() {
  497. depTypeList().then(response => {
  498. this.depTypeList = response.data.data;
  499. }).catch(() => {});
  500. },
  501. getList() {
  502. this.listLoading = true
  503. goodsList(this.listQuery).then(response => {
  504. this.list = response.data.data.items
  505. this.total = response.data.data.total
  506. this.listLoading = false
  507. }).catch(() => {})
  508. },
  509. getItemList() {
  510. this.listLoading = true
  511. exchangeHistory(this.itemListQuery).then(response => {
  512. this.itemList = response.data.data.items
  513. this.itemLotal = response.data.data.total
  514. this.listLoading = false
  515. }).catch(() => {})
  516. },
  517. handleFilter() {
  518. this.listQuery.page = 1
  519. this.getList()
  520. },
  521. handleSizeChange(val) {
  522. this.listQuery.limit = val
  523. this.getList()
  524. },
  525. handleCurrentChange(val) {
  526. this.listQuery.page = val
  527. this.getList()
  528. },
  529. itemHandleFilter() {
  530. this.itemListQuery.page = 1
  531. this.getItemList()
  532. },
  533. itemHandleSizeChange(val) {
  534. this.itemListQuery.limit = val
  535. this.getItemList()
  536. },
  537. itemHandleCurrentChange(val) {
  538. this.itemListQuery.page = val
  539. this.getItemList()
  540. },
  541. handleView(row) {
  542. this.itemListQuery.skuId = row.skuId;
  543. this.getItemList();
  544. this.dialogListVisible = true
  545. },
  546. }
  547. }
  548. </script>
  549. <style>
  550. .ad-avatar-uploader .el-upload {
  551. border: 1px dashed #d9d9d9;
  552. border-radius: 6px;
  553. cursor: pointer;
  554. position: relative;
  555. overflow: hidden;
  556. }
  557. .ad-avatar-uploader .el-upload:hover {
  558. border-color: #409EFF;
  559. }
  560. .ad-avatar-uploader-icon {
  561. font-size: 28px;
  562. color: #8c939d;
  563. width: 178px;
  564. height: 178px;
  565. line-height: 178px;
  566. text-align: center;
  567. }
  568. .ad-avatar {
  569. display: block;
  570. }
  571. </style>