index.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. <template>
  2. <div class="app-container">
  3. <!-- 查询 -->
  4. <el-card class="search-card" shadow="never">
  5. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
  6. <el-form-item prop="goodsName">
  7. <el-input v-model="queryParams.goodsName" placeholder="请输入商品名称" clearable @keyup.enter.native="handleQuery" />
  8. </el-form-item>
  9. <el-form-item prop="brandId">
  10. <el-select v-model="queryParams.brandId" placeholder="请选择商品品牌" clearable>
  11. <el-option v-for="brand in brandList" :key="brand.id" :label="brand.brandName" :value="brand.id" />
  12. </el-select>
  13. </el-form-item>
  14. <el-form-item prop="goodsCategoryId">
  15. <el-select v-model="queryParams.goodsCategoryId" placeholder="请选择商品分类" clearable>
  16. <el-option v-for="category in categoryList" :key="category.id" :label="category.categoryName" :value="category.id" />
  17. </el-select>
  18. </el-form-item>
  19. <el-form-item prop="goodsCode">
  20. <el-input v-model="queryParams.goodsCode" placeholder="请输入商品编号" clearable @keyup.enter.native="handleQuery" />
  21. </el-form-item>
  22. <el-form-item>
  23. <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  24. <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  25. </el-form-item>
  26. </el-form>
  27. </el-card>
  28. <el-row :gutter="10" class="mb8">
  29. <el-col :span="1.5">
  30. <el-button type="primary" plain icon="el-icon-plus" @click="handleAdd" v-hasPermi="['goods:commonGoods:add']"
  31. v-if="userInfoVO.userType == '00'">新增</el-button>
  32. </el-col>
  33. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  34. </el-row>
  35. <!-- 列表 -->
  36. <Page uri="/mapi/core/goods/list" :request-params="queryParams" ref="pagination">
  37. <el-table-column label="商品ID" align="center" prop="id" width="100" />
  38. <el-table-column label="商品图片" align="left" width="120">
  39. <template slot-scope="scope">
  40. <div class="goods-info">
  41. <el-image style="width: 90px; height: 90px; margin-right: 10px;" :src="scope.row.goodsImg" :preview-src-list="[scope.row.goodsImg]">
  42. </el-image>
  43. </div>
  44. </template>
  45. </el-table-column>
  46. <el-table-column label="商品编码" align="center" prop="goodsCode" width="120" />
  47. <el-table-column label="商品名称" align="center" prop="goodsName" min-width="180" />
  48. <el-table-column label="商品品牌" align="center" prop="brandName" width="150" />
  49. <el-table-column label="商品分类" align="center" prop="categoryName" width="150">
  50. <template slot-scope="scope">
  51. <el-tag size="mini" type="info" v-if="scope.row.goodsCategoryId" style="margin-left: 5px;">
  52. <span v-if="scope.row.goodsCategoryId == item.id" v-for="item in categoryList" :key="item.id">
  53. {{item.categoryName}}
  54. </span>
  55. </el-tag>
  56. </template>
  57. </el-table-column>
  58. <!-- <el-table-column label="价格/库存" align="center" width="200">
  59. <template slot-scope="scope">
  60. <div v-if="scope.row.skuList && scope.row.skuList.length > 0">
  61. <div>销售价:¥{{getMinMaxPrice(scope.row.skuList, 'salePrice')}}</div>
  62. <div>批发价:¥{{getMinMaxPrice(scope.row.skuList, 'purchasePrice')}}</div>
  63. <div>总库存:{{getTotalStock(scope.row.skuList)}}</div>
  64. </div>
  65. <div v-else>
  66. <div>暂无价格信息</div>
  67. </div>
  68. </template>
  69. </el-table-column> -->
  70. <el-table-column label="规格" align="center" width="120">
  71. <template slot-scope="scope">
  72. <el-button type="text" @click="openSkuDialog(scope.row)">
  73. <span>{{scope.row.goodsItemNum}}种规格</span>
  74. <i class="el-icon-arrow-right"></i>
  75. </el-button>
  76. </template>
  77. </el-table-column>
  78. <el-table-column label="支付方式" align="center" width="150">
  79. <template slot-scope="scope">
  80. <div>
  81. <el-tag v-if="scope.row.isBalancePay === 'Y'" size="mini" type="success">支持余额</el-tag>
  82. <el-tag v-else size="mini" type="info">不支持余额</el-tag>
  83. </div>
  84. <div style="margin-top: 5px;">
  85. <el-tag v-if="scope.row.isSell === 'Y'" size="mini" type="warning">促销标识</el-tag>
  86. </div>
  87. </template>
  88. </el-table-column>
  89. <el-table-column label="状态" align="center" width="120">
  90. <template slot-scope="scope">
  91. <div style="margin-top: 5px;">
  92. <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)">
  93. </el-switch>
  94. </div>
  95. </template>
  96. </el-table-column>
  97. <el-table-column label="排序" align="center" width="120">
  98. <template slot-scope="scope">
  99. <div>排序:{{scope.row.sort}}</div>
  100. </template>
  101. </el-table-column>
  102. <el-table-column label="创建时间" align="center" prop="createTime" width="150"></el-table-column>
  103. <el-table-column label="操作" align="center" width="200" fixed="right">
  104. <template slot-scope="scope">
  105. <el-button type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['goods:commonGoods:edit']"
  106. v-if="userInfoVO.userType == '00'">修改</el-button>
  107. <el-button type="text" icon="el-icon-view" v-if="goodsType != 1" @click="gotoComment(scope.row)">评论</el-button>
  108. <el-button type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['goods:commonGoods:remove']"
  109. v-if="userInfoVO.userType == '00'">删除</el-button>
  110. </template>
  111. </el-table-column>
  112. </Page>
  113. <!-- 分页 -->
  114. <!-- 添加或修改商品管理对话框 -->
  115. <el-dialog :title="title" :visible.sync="open" width="70%" append-to-body>
  116. <el-form ref="form" :model="form" :rules="rules" label-width="120px">
  117. <el-tabs v-model="activeTab">
  118. <!-- 基本信息 tab -->
  119. <el-tab-pane label="基本信息" name="basic">
  120. <el-row :gutter="15">
  121. <el-col :span="12">
  122. <el-form-item label="商品名称" prop="goodsName">
  123. <el-input v-model="form.goodsName" placeholder="请输入商品名称" />
  124. </el-form-item>
  125. </el-col>
  126. <el-col :span="12">
  127. <el-form-item label="商品编码" prop="goodsCode">
  128. <el-input v-model="form.goodsCode" placeholder="请输入商品编码" />
  129. </el-form-item>
  130. </el-col>
  131. </el-row>
  132. <el-row :gutter="15">
  133. <el-col :span="12">
  134. <el-form-item label="品牌" prop="brandId">
  135. <el-select v-model="form.brandId" placeholder="请选择品牌" clearable>
  136. <el-option v-for="brand in brandList" :key="brand.id" :label="brand.brandName" :value="brand.id" />
  137. </el-select>
  138. </el-form-item>
  139. </el-col>
  140. <el-col :span="12">
  141. <el-form-item label="商品分类" prop="goodsCategoryId">
  142. <el-select v-model="form.goodsCategoryId" placeholder="请选择商品分类" clearable>
  143. <el-option v-for="category in categoryList" :key="category.id" :label="category.categoryName" :value="category.id" />
  144. </el-select>
  145. </el-form-item>
  146. </el-col>
  147. </el-row>
  148. <el-form-item label="商品图片" prop="goodsImg">
  149. <image-upload v-model="form.goodsImg" />
  150. </el-form-item>
  151. <el-form-item label="商品图片集" prop="goodsImgs">
  152. <image-upload v-model="form.goodsImgs" :limit="limit" />
  153. </el-form-item>
  154. <el-form-item label="商品简介" prop="goodsSynopsis">
  155. <editor v-model="form.goodsSynopsis" :min-height="192" />
  156. </el-form-item>
  157. </el-tab-pane>
  158. <!-- 规格设置 tab -->
  159. <el-tab-pane label="规格设置" name="sku">
  160. <el-form-item label="规格类型">
  161. <el-radio-group v-model="form.skuType" @change="handleSkuTypeChange">
  162. <el-radio :label="0">单规格</el-radio>
  163. <el-radio :label="1">多规格</el-radio>
  164. </el-radio-group>
  165. </el-form-item>
  166. <!-- 单规格设置 -->
  167. <template v-if="form.skuType === 0">
  168. <el-form-item label="销售价格" prop="singleSkuPrice">
  169. <el-input-number v-model="form.singleSkuPrice" :precision="2" :step="0.1" :min="0" />
  170. </el-form-item>
  171. <el-form-item label="成本价格" prop="singleSkuCostPrice">
  172. <el-input-number v-model="form.singleSkuCostPrice" :precision="2" :step="0.1" :min="0" />
  173. </el-form-item>
  174. <el-form-item label="采购价格" prop="singleSkuPurchasePrice">
  175. <el-input-number v-model="form.singleSkuPurchasePrice" :precision="2" :step="0.1" :min="0" />
  176. </el-form-item>
  177. <el-form-item label="库存数量" prop="singleSkuStock">
  178. <el-input-number v-model="form.singleSkuStock" :min="0" :step="1" />
  179. </el-form-item>
  180. </template>
  181. <!-- 多规格设置 -->
  182. <template v-if="form.skuType === 1">
  183. <!-- 规格项设置 -->
  184. <div class="spec-group" v-for="(spec, index) in form.specList" :key="index">
  185. <el-row :gutter="10">
  186. <el-col :span="8">
  187. <el-form-item :label="'规格项' + (index + 1)">
  188. <el-input v-model="spec.name" placeholder="如: 颜色、尺码" />
  189. </el-form-item>
  190. </el-col>
  191. <el-col :span="14">
  192. <el-form-item label="规格值">
  193. <el-tag :key="tag" v-for="tag in spec.specValues" closable :disable-transitions="false"
  194. @close="handleRemoveSpecValue(spec.specValues, tag)">
  195. {{tag}}
  196. </el-tag>
  197. <el-input class="input-new-tag" v-if="spec.inputVisible" v-model="spec.inputValue" ref="tagInput" size="small"
  198. @keyup.enter.native="handleInputConfirm(spec, index)" @blur="handleInputConfirm(spec, index)">
  199. </el-input>
  200. <el-button v-else class="button-new-tag" size="small" @click="showInput(spec, index)">
  201. + 添加规格值
  202. </el-button>
  203. </el-form-item>
  204. </el-col>
  205. <el-col :span="2">
  206. <el-button type="text" @click="removeSpec(index)" icon="el-icon-delete">删除</el-button>
  207. </el-col>
  208. </el-row>
  209. </div>
  210. <el-button type="primary" plain @click="addSpec">添加规格项</el-button>
  211. <!-- 规格组合列表 -->
  212. <el-table :data="form.skuList" border style="margin-top: 20px">
  213. <el-table-column v-for="(spec, index) in form.specList" :key="index" :label="spec.name" align="center">
  214. <template slot-scope="scope">
  215. {{scope.row.specValLists[index]}}
  216. </template>
  217. </el-table-column>
  218. <el-table-column label="销售价格" align="center" width="150">
  219. <template slot-scope="scope">
  220. <el-input-number v-model="scope.row.salePrice" :precision="2" :step="0.1" :min="0" controls-position="right" size="mini" />
  221. </template>
  222. </el-table-column>
  223. <el-table-column label="成本价格" align="center" width="150">
  224. <template slot-scope="scope">
  225. <el-input-number v-model="scope.row.costPrice" :precision="2" :step="0.1" :min="0" controls-position="right" size="mini" />
  226. </template>
  227. </el-table-column>
  228. <el-table-column label="采购价格" align="center" width="150">
  229. <template slot-scope="scope">
  230. <el-input-number v-model="scope.row.purchasePrice" :precision="2" :step="0.1" :min="0" controls-position="right" size="mini" />
  231. </template>
  232. </el-table-column>
  233. <el-table-column label="库存" align="center" width="150">
  234. <template slot-scope="scope">
  235. <el-input-number v-model="scope.row.stock" :min="0" :step="1" controls-position="right" size="mini" />
  236. </template>
  237. </el-table-column>
  238. </el-table>
  239. </template>
  240. </el-tab-pane>
  241. <!-- 其他设置 tab -->
  242. <el-tab-pane label="其他设置" name="other">
  243. <el-form-item label="促销标识" prop="isSell">
  244. <el-radio-group v-model="form.isSell">
  245. <el-radio-button v-for="dict in dict.type.sys_yes_no" :label="dict.value" :key="dict.code">
  246. {{dict.label}}
  247. </el-radio-button>
  248. </el-radio-group>
  249. </el-form-item>
  250. <el-form-item label="余额支付" prop="isBalancePay">
  251. <el-radio-group v-model="form.isBalancePay">
  252. <el-radio-button v-for="dict in dict.type.sys_yes_no" :label="dict.value" :key="dict.code">
  253. {{dict.label}}
  254. </el-radio-button>
  255. </el-radio-group>
  256. </el-form-item>
  257. <el-form-item label="显示顺序" prop="sort">
  258. <el-input-number v-model="form.sort" :min="0" :step="1" />
  259. </el-form-item>
  260. </el-tab-pane>
  261. </el-tabs>
  262. </el-form>
  263. <div slot="footer" class="dialog-footer">
  264. <el-button type="primary" @click="submitForm">确 定</el-button>
  265. <el-button @click="cancel">取 消</el-button>
  266. </div>
  267. </el-dialog>
  268. <!-- 添加规格弹窗组件 -->
  269. <goods-sku-dialog ref="skuDialog"></goods-sku-dialog>
  270. </div>
  271. </template>
  272. <script>
  273. import { listGoods, getGoods, delGoods, addGoods, updateGoods, updateGoodsStatus } from '@/api/core/goods'
  274. import { listCategory } from '@/api/core/category'
  275. import { listBrand } from '@/api/core/brand'
  276. import { status } from 'nprogress'
  277. import GoodsSkuDialog from '@/components/GoodsSkuDialog'
  278. export default {
  279. name: 'appGoods',
  280. components: {
  281. GoodsSkuDialog
  282. },
  283. dicts: ['goods_type', 'sys_yes_no'],
  284. data() {
  285. return {
  286. userInfoVO: null,
  287. uploadAction: `${process.env.VUE_APP_BASE_API}` + '/common/uploadOSS',
  288. imgsList: [],
  289. fileSize: 3,
  290. // 遮罩层
  291. loading: true,
  292. // 选中数组
  293. ids: [],
  294. // 非单个禁用
  295. single: true,
  296. // 非多个禁用
  297. multiple: true,
  298. // 显示搜索条件
  299. showSearch: true,
  300. // 总条数
  301. total: 0,
  302. // 商品管理表格数据
  303. goodsList: [],
  304. // 弹出层标题
  305. title: '',
  306. // 是否显示弹出层
  307. open: false,
  308. // 查询参数
  309. queryParams: {
  310. goodsName: null,
  311. goodsType: null,
  312. status: null,
  313. goodsCategoryId: null,
  314. goodsCode: null
  315. },
  316. queryParams2: {
  317. pageNum: 1,
  318. pageSize: 9999,
  319. status: '0',
  320. goodsType: null
  321. },
  322. // 表单参数
  323. form: {
  324. id: null,
  325. goodsName: null,
  326. goodsCategoryId: null,
  327. goodsSynopsis: null,
  328. brandId: null,
  329. goodsImg: null,
  330. goodsImgs: [],
  331. goodsCode: null,
  332. isSell: 'N',
  333. isCashPay: 'Y',
  334. cashMoney: null,
  335. isBalancePay: 'Y',
  336. createBy: null,
  337. createById: null,
  338. createTime: null,
  339. updateBy: null,
  340. updateById: null,
  341. updateTime: null,
  342. remark: null,
  343. skuType: 0,
  344. status: '0',
  345. specList: [],
  346. skuList: [],
  347. sort: '0',
  348. singleSkuPrice: 0,
  349. singleSkuCostPrice: 0,
  350. singleSkuPurchasePrice: 0,
  351. singleSkuStock: 0
  352. },
  353. // 表单校验
  354. rules: {
  355. goodsName: [
  356. {
  357. required: true,
  358. message: '商品名称不能为空',
  359. trigger: 'blur'
  360. }
  361. ],
  362. goodsCode: [
  363. {
  364. required: true,
  365. message: '商品编号不能为空',
  366. trigger: 'blur'
  367. }
  368. ],
  369. goodsSynopsis: [
  370. {
  371. required: true,
  372. message: '商品简介不能为空',
  373. trigger: 'blur'
  374. }
  375. ],
  376. goodsImg: [
  377. {
  378. required: true,
  379. message: '商品头图不能为空',
  380. trigger: 'blur'
  381. }
  382. ],
  383. goodsImgs: [
  384. {
  385. required: true,
  386. message: '商品图片集不能为空',
  387. trigger: 'blur'
  388. }
  389. ],
  390. isBalancePay: [
  391. {
  392. required: true,
  393. message: '是否余额支付不能为空',
  394. trigger: 'blur'
  395. }
  396. ],
  397. isCashPay: [
  398. {
  399. required: true,
  400. message: '是否现金支付不能为空',
  401. trigger: 'blur'
  402. }
  403. ],
  404. isSell: [
  405. {
  406. required: true,
  407. message: '是否促销不能为空',
  408. trigger: 'blur'
  409. }
  410. ],
  411. cashMoney: [
  412. {
  413. required: true,
  414. message: '现金金额不能为空',
  415. trigger: 'blur'
  416. }
  417. ]
  418. },
  419. categoryList: [],
  420. brandList: [],
  421. goodsType: '',
  422. limit: 9,
  423. activeTab: 'basic',
  424. skuType: 0
  425. }
  426. },
  427. created() {
  428. this.handleUser()
  429. this.goodsType = this.getUrlParam('goodsType')
  430. this.form.goodsType = this.goodsType
  431. this.getList()
  432. this.getCategoryList()
  433. this.getBrandList()
  434. },
  435. methods: {
  436. handleUser() {
  437. this.userInfoVO = this.getUserInfo()
  438. },
  439. getBrandList() {
  440. listBrand({ ...this.queryParams2, goodsType: this.goodsType }).then(response => {
  441. this.brandList = response.rows || []
  442. })
  443. },
  444. getCategoryList() {
  445. listCategory({ ...this.queryParams2, goodsType: this.goodsType }).then(response => {
  446. this.categoryList = response.rows || []
  447. })
  448. },
  449. /** 查询商品管理列表 */
  450. getList() {
  451. this.queryParams.goodsType = this.goodsType
  452. this.$nextTick(() => {
  453. this.$refs.pagination.handleSearch(true)
  454. })
  455. },
  456. // 取消按钮
  457. cancel() {
  458. this.open = false
  459. this.reset()
  460. },
  461. // 表单重置
  462. reset() {
  463. this.form = {
  464. id: null,
  465. goodsType: this.goodsType,
  466. goodsName: null,
  467. goodsCategoryId: null,
  468. goodsSynopsis: null,
  469. brandId: null,
  470. goodsImg: null,
  471. goodsImgs: [],
  472. goodsCode: null,
  473. isSell: 'N',
  474. skuType: 0,
  475. isCashPay: 'Y',
  476. cashMoney: null,
  477. isBalancePay: 'Y',
  478. createBy: null,
  479. createById: null,
  480. createTime: null,
  481. updateBy: null,
  482. updateById: null,
  483. updateTime: null,
  484. remark: null,
  485. status: '0',
  486. sort: '0',
  487. specList: [],
  488. skuList: [],
  489. singleSkuPrice: 0,
  490. singleSkuCostPrice: 0,
  491. singleSkuPurchasePrice: 0,
  492. singleSkuStock: 0
  493. }
  494. this.resetForm('form')
  495. },
  496. /** 搜索按钮操作 */
  497. handleQuery() {
  498. this.getList()
  499. },
  500. /** 重置按钮操作 */
  501. resetQuery() {
  502. this.resetForm('queryForm')
  503. this.handleQuery()
  504. },
  505. // 多选框选中数据
  506. handleSelectionChange(selection) {
  507. this.ids = selection.map((item) => item.id)
  508. this.single = selection.length !== 1
  509. this.multiple = !selection.length
  510. },
  511. /** 新增按钮操作 */
  512. handleAdd() {
  513. this.reset()
  514. this.open = true
  515. this.title = '添加商品管理'
  516. // 重新获取品牌和分类列表
  517. this.getBrandList()
  518. this.getCategoryList()
  519. },
  520. /** 修改按钮操作 */
  521. handleUpdate(row) {
  522. this.reset()
  523. this.imgsList = []
  524. const id = row.id || this.ids
  525. getGoods(id).then((response) => {
  526. this.form = response.data
  527. this.form.brandId = Number(this.form.brandId)
  528. this.form.goodsCategoryId = Number(this.form.goodsCategoryId)
  529. if (this.form.goodsImgs != null && this.form.goodsImgs != '') {
  530. this.form.goodsImgs.split(',').forEach((item) => {
  531. const vo = {
  532. name: '',
  533. url: item
  534. }
  535. this.imgsList.push(vo)
  536. })
  537. }
  538. this.open = true
  539. this.title = '修改商品管理'
  540. })
  541. },
  542. /** 提交按钮 */
  543. submitForm() {
  544. this.$refs['form'].validate(valid => {
  545. if (valid) {
  546. const formData = { ...this.form }
  547. // 处理SKU数据
  548. if (this.form.skuType === 0) {
  549. formData.skuList = [{
  550. specValLists: [],
  551. salePrice: this.form.singleSkuPrice,
  552. costPrice: this.form.singleSkuCostPrice,
  553. purchasePrice: this.form.singleSkuPurchasePrice,
  554. stock: this.form.singleSkuStock
  555. }]
  556. } else {
  557. formData.skuList = this.form.skuList
  558. }
  559. // 提交数据
  560. if (formData.id) {
  561. updateGoods(formData).then(response => {
  562. this.$modal.msgSuccess('修改成功')
  563. this.open = false
  564. this.getList()
  565. })
  566. } else {
  567. addGoods(formData).then(response => {
  568. this.$modal.msgSuccess('新增成功')
  569. this.open = false
  570. this.getList()
  571. })
  572. }
  573. }
  574. })
  575. },
  576. /** 删除按钮操作 */
  577. handleDelete(row) {
  578. const ids = row.id || this.ids
  579. this.$modal
  580. .confirm('是否确认删除商品管理编号为"' + ids + '"的数据项?')
  581. .then(function () {
  582. return delGoods(ids)
  583. })
  584. .then(() => {
  585. this.getList()
  586. this.$modal.msgSuccess('删除成功')
  587. })
  588. .catch(() => { })
  589. },
  590. /** 导出按钮操作 */
  591. handleExport() {
  592. this.download(
  593. 'commonGoods/commonGoods/export',
  594. {
  595. ...this.queryParams
  596. },
  597. `goods_${new Date().getTime()}.xlsx`
  598. )
  599. },
  600. checkClose(done) {
  601. this.$confirm('是否关闭表单,关闭后数据将丢失?')
  602. .then(function () {
  603. done()
  604. })
  605. .then(() => { })
  606. .catch(() => { })
  607. },
  608. handleStatusChange(row) {
  609. let text = row.status === '0' ? '启用' : '停用'
  610. this.$confirm('确认要' + text + '吗?')
  611. .then(function () {
  612. return updateGoodsStatus(row.id, row.status)
  613. })
  614. .then(() => {
  615. this.$modal.msgSuccess(text + '成功')
  616. })
  617. .catch(function () {
  618. row.status = row.status === '0' ? '1' : '0'
  619. })
  620. },
  621. /** 设置物料 */
  622. handleSkuUpdate(row) {
  623. this.openSkuDialog(row)
  624. },
  625. openSkuDialog(row) {
  626. this.$refs.skuDialog.show(row.id)
  627. },
  628. handleRemove(file, fileList) {
  629. console.log(fileList)
  630. this.imgsList = []
  631. fileList.forEach((item) => {
  632. const vo = {
  633. name: item.name,
  634. url: item.url
  635. }
  636. this.imgsList.push(vo)
  637. console.log(fileList)
  638. })
  639. },
  640. // 上传前校检格式和大小
  641. handleBeforeUpload(file) {
  642. // 校检文件大小
  643. if (this.fileSize) {
  644. const isLt = file.size / 1024 / 1024 < this.fileSize
  645. if (!isLt) {
  646. this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`)
  647. return false
  648. }
  649. }
  650. return true
  651. },
  652. handleUploadSuccessByImg(res, file) {
  653. // 获取富文本组件实例
  654. let quill = this.Quill
  655. // 如果上传成功
  656. if (res.code == 200) {
  657. // 获取光标所在位置
  658. this.form.goodsImg = res.data.src
  659. this.$forceUpdate()
  660. } else {
  661. this.$message.error('图片插入失败')
  662. }
  663. },
  664. handleUploadSuccessByImgs(res, file, fileList) {
  665. // 获取富文本组件实例
  666. let quill = this.Quill
  667. // 如果上传成功
  668. if (res.code == 200) {
  669. const vo = {
  670. name: '',
  671. url: res.data.src
  672. }
  673. this.imgsList.push(vo)
  674. this.$forceUpdate()
  675. } else {
  676. this.$message.error('图片插入失败')
  677. }
  678. },
  679. handleUploadError() {
  680. this.$message.error('图片插入失败')
  681. },
  682. handleExceed() {
  683. //提示最多只能上传9个
  684. this.$message.error('最多上传9个图片!')
  685. },
  686. gotoComment(row) {
  687. this.$router.push({ path: `/platform/goodsComment/appGoodsComment?goodsType=${this.goodsType}&goodsId=${row.id}` })
  688. },
  689. handleSkuTypeChange(val) {
  690. if (val === 0) {
  691. this.form.specList = []
  692. this.form.skuList = []
  693. } else {
  694. // 如果已有规格数据,重新生成组合
  695. if (this.form.specList.length > 0) {
  696. this.generateSkuList()
  697. }
  698. }
  699. },
  700. addSpec() {
  701. if (!this.form.specList) {
  702. this.form.specList = []
  703. }
  704. this.form.specList.push({
  705. name: '',
  706. specValues: [],
  707. inputVisible: false,
  708. inputValue: ''
  709. })
  710. },
  711. removeSpec(index) {
  712. this.form.specList.splice(index, 1)
  713. this.$nextTick(() => {
  714. this.generateSkuList()
  715. })
  716. },
  717. handleRemoveSpecValue(values, tag) {
  718. values.splice(values.indexOf(tag), 1)
  719. this.$nextTick(() => {
  720. this.generateSkuList()
  721. })
  722. },
  723. showInput(spec, index) {
  724. // 先重置其他所有输入框的状态
  725. this.form.specList.forEach(item => {
  726. this.$set(item, 'inputVisible', false);
  727. });
  728. // 设置当前输入框可见
  729. this.$set(spec, 'inputVisible', true);
  730. // 强制更新视图
  731. this.$forceUpdate();
  732. // 等待 DOM 更新后设置焦点
  733. this.$nextTick(() => {
  734. if (this.$refs.tagInput && this.$refs.tagInput.length > 0) {
  735. const inputs = this.$refs.tagInput;
  736. // 找到最后一个输入框(应该是刚刚显示的那个)
  737. const input = Array.isArray(inputs) ? inputs[inputs.length - 1] : inputs;
  738. if (input && input.$el.querySelector('input')) {
  739. input.$el.querySelector('input').focus();
  740. }
  741. }
  742. });
  743. },
  744. handleInputConfirm(spec, index) {
  745. let inputValue = spec.inputValue
  746. if (inputValue && spec.specValues.indexOf(inputValue) === -1) {
  747. if (!spec.specValues) {
  748. spec.specValues = []
  749. }
  750. spec.specValues.push(inputValue)
  751. this.$nextTick(() => {
  752. this.generateSkuList()
  753. })
  754. }
  755. spec.inputVisible = false
  756. spec.inputValue = ''
  757. },
  758. generateSkuList() {
  759. const specs = this.form.specList
  760. if (specs.length === 0 || specs.some(spec => spec.specValues.length === 0)) {
  761. this.form.skuList = []
  762. return
  763. }
  764. // 获取所有规格值的组合
  765. const values = specs.map(spec => spec.specValues)
  766. const combinations = this.cartesianProduct(values)
  767. // 保存现有的价格和库存数据
  768. const existingSkus = this.form.skuList || []
  769. // 生成新的SKU列表,保留已存在的价格和库存
  770. this.form.skuList = combinations.map(specs => {
  771. // 查找是否存在相同规格的旧数据
  772. const existingSku = existingSkus.find(old =>
  773. JSON.stringify(old.specValLists) === JSON.stringify(specs)
  774. )
  775. return {
  776. specValLists: specs,
  777. salePrice: existingSku ? existingSku.salePrice : 0,
  778. costPrice: existingSku ? existingSku.costPrice : 0,
  779. purchasePrice: existingSku ? existingSku.purchasePrice : 0,
  780. stock: existingSku ? existingSku.stock : 0
  781. }
  782. })
  783. },
  784. cartesianProduct(arrays) {
  785. return arrays.reduce((a, b) => {
  786. return a.map(x => {
  787. return b.map(y => {
  788. return Array.isArray(x) ? [...x, y] : [x, y]
  789. })
  790. }).flat()
  791. }, [[]])
  792. },
  793. /**
  794. * @description 添加规格值
  795. * @param {number} index 规格索引
  796. */
  797. addSpecValue(index) {
  798. // 确保规格值数组已初始化
  799. if (!this.form.specList[index].specValueList) {
  800. this.$set(this.form.specList[index], 'specValueList', [])
  801. }
  802. this.form.specList[index].specValueList.push({
  803. specValue: '',
  804. image: ''
  805. })
  806. },
  807. getMinMaxPrice(skuList, priceType) {
  808. if (!skuList || skuList.length === 0) return '0.00';
  809. const prices = skuList.map(sku => sku[priceType] || 0);
  810. const min = Math.min(...prices);
  811. const max = Math.max(...prices);
  812. return min === max ? min.toFixed(2) : `${min.toFixed(2)} ~ ${max.toFixed(2)}`;
  813. },
  814. getTotalStock(skuList) {
  815. if (!skuList || skuList.length === 0) return 0;
  816. return skuList.reduce((total, sku) => total + (sku.stock || 0), 0);
  817. }
  818. }
  819. }
  820. </script>
  821. <style lang="scss" scoped>
  822. .spec-group {
  823. padding: 10px;
  824. border: 1px solid #EBEEF5;
  825. margin-bottom: 10px;
  826. }
  827. .el-tag + .el-tag {
  828. margin-left: 10px;
  829. }
  830. .button-new-tag {
  831. margin-left: 10px;
  832. height: 32px;
  833. line-height: 30px;
  834. padding-top: 0;
  835. padding-bottom: 0;
  836. }
  837. .input-new-tag {
  838. width: 90px;
  839. margin-left: 10px;
  840. vertical-align: bottom;
  841. }
  842. .goods-info {
  843. display: flex;
  844. align-items: flex-start;
  845. padding: 10px 0;
  846. }
  847. .goods-detail {
  848. flex: 1;
  849. overflow: hidden;
  850. }
  851. .goods-name {
  852. font-size: 14px;
  853. font-weight: bold;
  854. color: #303133;
  855. margin-bottom: 5px;
  856. overflow: hidden;
  857. text-overflow: ellipsis;
  858. display: -webkit-box;
  859. -webkit-line-clamp: 2;
  860. -webkit-box-orient: vertical;
  861. }
  862. .goods-code {
  863. font-size: 12px;
  864. color: #909399;
  865. margin-bottom: 5px;
  866. }
  867. .goods-category {
  868. margin-top: 5px;
  869. }
  870. </style>