form-club-device.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. <template>
  2. <div class="club-device">
  3. <template v-for="formItem in formList">
  4. <div :key="formItem.uid" class="device-section">
  5. <span
  6. class="remove-btn"
  7. @click="removeOne(formItem)"
  8. v-if="formList.length > 1"
  9. >删除这台设备</span
  10. >
  11. <el-form :model="formItem" :rules="rules" ref="form">
  12. <el-form-item prop="productName" :label="`设备名称${formItem.uuid}:`">
  13. <el-select
  14. v-model="formItem.productName"
  15. filterable
  16. allow-create
  17. placeholder="请输入新设备名称或选择已有设备"
  18. @change="onProductNameChange(formItem, $event)"
  19. >
  20. <el-option
  21. v-for="item in deviceList"
  22. :key="item.productTypeId"
  23. :label="item.name"
  24. :value="item.productTypeId"
  25. />
  26. </el-select>
  27. </el-form-item>
  28. <el-form-item prop="productImage" label="设备图片:">
  29. <br />
  30. <el-input v-show="false" v-model="formItem.productImage"></el-input>
  31. <SimpleUploadImage
  32. :disabled="Boolean(formItem.productTypeId)"
  33. :limit="1"
  34. :image-list="formItem.productImageList"
  35. :before-upload="beforeProductImageUpload"
  36. @success="uploadProductImageSuccess(formItem, $event)"
  37. @remove="handleProductImageRemove(formItem, $event)"
  38. />
  39. </el-form-item>
  40. <el-form-item label="所属品牌:" prop="brandId" v-if="editPrams">
  41. <el-select v-model="formItem.brandId" placeholder="请选择品牌">
  42. <el-option
  43. v-for="item in brandList"
  44. :key="item.id"
  45. :label="item.name"
  46. :value="item.id"
  47. />
  48. </el-select>
  49. </el-form-item>
  50. <el-form-item prop="purchaseWay" label="购买渠道:">
  51. <el-input
  52. placeholder="请输入购买渠道"
  53. v-model="formItem.purchaseWay"
  54. ></el-input>
  55. </el-form-item>
  56. <el-form-item prop="invoiceImage" label="发票:">
  57. <br />
  58. <el-input v-show="false" v-model="formItem.invoiceImage"></el-input>
  59. <SimpleUploadImage
  60. :limit="1"
  61. :image-list="formItem.invoiceImageList"
  62. :before-upload="beforeInvoiceImageUpload"
  63. @success="uploadInvoiceImageSuccess(formItem, $event)"
  64. @remove="handleInvoiceImageRemove(formItem, $event)"
  65. />
  66. </el-form-item>
  67. <el-form-item prop="snCode" label="设备SN码:">
  68. <el-input
  69. placeholder="请输入设备SN码"
  70. v-model="formItem.snCode"
  71. ></el-input>
  72. </el-form-item>
  73. <el-form-item prop="paramList" label="设备参数:">
  74. <br />
  75. <div class="device-param-list">
  76. <span class="add-param" @click="insertParam(formItem)"
  77. >添加参数</span
  78. >
  79. <template v-for="(param, index) in formItem.paramList">
  80. <div :key="index">
  81. <div class="param flex justify-between mb-4">
  82. <el-input
  83. style="width: 40%"
  84. placeholder="例如:品牌"
  85. class="mr-2"
  86. v-model="param.paramName"
  87. ></el-input>
  88. <el-input
  89. placeholder="请输入参数信息"
  90. v-model="param.paramContent"
  91. ></el-input>
  92. <span
  93. class="remove el-icon-close"
  94. @click="removeParam(formItem, index)"
  95. v-if="formItem.paramList.length > 4"
  96. ></span>
  97. </div>
  98. </div>
  99. </template>
  100. </div>
  101. </el-form-item>
  102. </el-form>
  103. <el-divider></el-divider>
  104. </div>
  105. </template>
  106. <div class="add-device" @click="insertOne" v-if="formType !== 'edit'">
  107. <div class="add-icon"></div>
  108. 添加设备
  109. </div>
  110. <SimpleDialog
  111. v-if="formType !== 'edit'"
  112. v-model="active"
  113. @confirm="active = false"
  114. confirmText="好的"
  115. :cancel="false"
  116. description="请慎重填写设备信息,认证通过后将无法更改!"
  117. :center="true"
  118. />
  119. </div>
  120. </template>
  121. <script>
  122. import SimpleUploadImage from '@/components/SimpleUploadImage'
  123. import { mapGetters } from 'vuex'
  124. export default {
  125. components: {
  126. SimpleUploadImage,
  127. },
  128. props: {
  129. formType: {
  130. type: String,
  131. default: 'add',
  132. },
  133. },
  134. data() {
  135. const productNameValidate = (rule, value, callback) => {
  136. if (value.toString().length > 50) {
  137. callback(new Error('设备名称长度需要在50个字符内'))
  138. } else {
  139. callback()
  140. }
  141. }
  142. const paramListValidate = (rule, value, callback) => {
  143. const notEmptyList = value.filter(
  144. (item) => item.paramName.trim() && item.paramContent.trim()
  145. )
  146. if (notEmptyList.length === 0) {
  147. callback(new Error('参数列表不能为空'))
  148. } else if (notEmptyList.length < 4) {
  149. callback(new Error('请填写至少4项参数'))
  150. } else {
  151. callback()
  152. }
  153. }
  154. return {
  155. active: true,
  156. uuid: 0, // 表单id
  157. productImageList: [],
  158. rules: {
  159. productName: [
  160. { required: true, message: '设备名称不能为空', trigger: ['change'] },
  161. { validator: productNameValidate, trigger: ['change'] },
  162. ],
  163. productImage: [
  164. { required: true, message: '设备图片不能为空', trigger: ['change'] },
  165. ],
  166. brandId: [
  167. { required: true, message: '所属品牌不能为空', trigger: ['change'] },
  168. ],
  169. snCode: [
  170. { required: true, message: '设备SN码不能为空', trigger: ['blur'] },
  171. ],
  172. paramList: [
  173. { required: true, message: '参数不能为空', trigger: ['blur'] },
  174. { validator: paramListValidate, trigger: ['change'] },
  175. ],
  176. purchaseWay: [
  177. {
  178. required: true,
  179. message: '请输入购买渠道不能为空',
  180. trigger: ['blur'],
  181. },
  182. {
  183. max: 50,
  184. message: '最大长度为50个字符',
  185. trigger: ['blur'],
  186. },
  187. ],
  188. invoiceImage: [
  189. { required: true, message: '请上传发票', trigger: ['change'] },
  190. ],
  191. },
  192. formList: [],
  193. brandList: [],
  194. deviceList: [],
  195. editPrams: true
  196. }
  197. },
  198. computed: {
  199. ...mapGetters(['authUserId']),
  200. },
  201. created() {
  202. this.fetchBrandList()
  203. this.fetchDeviceList()
  204. this.initFormList()
  205. },
  206. methods: {
  207. // 表单验证
  208. validate() {
  209. this.$emit('step', this.formatFormList())
  210. return Promise.all(this.$refs.form.map((item) => item.validate()))
  211. },
  212. async init(formData) {
  213. console.log('formData', formData)
  214. const obj = {}
  215. const productImageList = [
  216. {
  217. name: '',
  218. url: formData?.productImage,
  219. },
  220. ]
  221. const invoiceImageList = [
  222. {
  223. name: '',
  224. url: formData.invoiceImage,
  225. },
  226. ]
  227. obj.uuid = ++this.uuid
  228. obj.productImageList = productImageList
  229. obj.invoiceImageList = invoiceImageList
  230. obj.productImage = formData.productImage
  231. obj.productName = formData.productName
  232. obj.snCode = formData.snCode
  233. obj.brandId = formData.brandId
  234. obj.productId = formData.productId
  235. obj.productTypeId = formData.productTypeId
  236. obj.purchaseWay = formData.purchaseWay
  237. obj.invoiceImage = formData.invoiceImage
  238. obj.paramList = formData.paramList
  239. this.formList.splice(0, 1, obj)
  240. console.log('formList', this.formList)
  241. },
  242. formatFormList() {
  243. const list = []
  244. this.formList.forEach((formItem) => {
  245. const obj = {}
  246. obj.productImage = formItem.productImage
  247. obj.productName = formItem.productName
  248. obj.snCode = formItem.snCode
  249. obj.brandId = formItem.brandId
  250. obj.productId = formItem.productId
  251. obj.source = 2
  252. obj.productTypeId = formItem.productTypeId
  253. obj.purchaseWay = formItem.purchaseWay
  254. obj.invoiceImage = formItem.invoiceImage
  255. obj.paramList = formItem.paramList
  256. list.push(obj)
  257. })
  258. return list
  259. },
  260. generateFormData() {
  261. return {
  262. uuid: ++this.uuid,
  263. authUserId: '',
  264. authId: '', // 授权id
  265. createBy: '', // 创建人id
  266. // 设备参数列表
  267. paramList: this.initParams(),
  268. productId: '', // 授权设备id
  269. productImage: '', // 设备图片
  270. productName: '', // 设备名称
  271. snCode: '', // 设备SN码
  272. brandId: '',
  273. productTypeId: '',
  274. purchaseWay: '', // 购买渠道
  275. invoiceImage: '', // 发票
  276. productImageList: [],
  277. invoiceImageList: [],
  278. }
  279. },
  280. generageProductParam() {
  281. return {
  282. paramContent: '',
  283. paramName: '',
  284. }
  285. },
  286. initParams() {
  287. const list = []
  288. for (let i = 0; i < 4; i++) {
  289. list.push(this.generageProductParam())
  290. }
  291. return list
  292. },
  293. insertParam(formItem) {
  294. formItem.paramList.push(this.generateFormData())
  295. },
  296. removeParam(formItem, index) {
  297. formItem.paramList.splice(index, 1)
  298. },
  299. initFormList() {
  300. this.formList.push(this.generateFormData())
  301. console.log(this.formList)
  302. },
  303. insertOne() {
  304. this.formList.push(this.generateFormData())
  305. },
  306. removeOne(formItem) {
  307. const index = this.formList.findIndex(
  308. (item) => item.uuid === formItem.uuid
  309. )
  310. this.formList.splice(index, 1)
  311. },
  312. onProductNameChange(formItem, value) {
  313. if (typeof value === 'number') {
  314. formItem.productTypeId = value
  315. const deviceInfo = this.deviceList.find(
  316. (item) => item.productTypeId === value
  317. )
  318. formItem.productImage = deviceInfo.image
  319. formItem.productImageList = [{ name: '', url: deviceInfo.image }]
  320. this.editPrams = false
  321. } else {
  322. formItem.productTypeId = ''
  323. formItem.productImage = ''
  324. formItem.productImageList = []
  325. this.editPrams = true
  326. }
  327. },
  328. // 获取品牌列表
  329. async fetchBrandList() {
  330. try {
  331. const res = await this.$http.api.fetchBrandList({
  332. type: 3,
  333. authUserId: this.authUserId,
  334. })
  335. this.brandList = res.data
  336. } catch (error) {
  337. console.log(error)
  338. }
  339. },
  340. // 获取设备列表
  341. async fetchDeviceList() {
  342. try {
  343. const res = await this.$http.api.fetchProductSelectList({
  344. authUserId: this.authUserId,
  345. })
  346. this.deviceList = res.data
  347. } catch (error) {
  348. console.log(error)
  349. }
  350. },
  351. // 产品图片上传
  352. beforeProductImageUpload(file) {
  353. const flag = file.size / 1024 / 1024 < 5
  354. if (!flag) {
  355. this.$message.error('上传产品图片大小不能超过 5MB!')
  356. }
  357. return flag
  358. },
  359. uploadProductImageSuccess(formItem, { response, file, fileList }) {
  360. formItem.productImageList = fileList
  361. formItem.productImage = response.data
  362. },
  363. handleProductImageRemove(formItem, { file, fileList }) {
  364. formItem.productImageList = fileList
  365. formItem.productImage = ''
  366. },
  367. // 发票上传
  368. beforeInvoiceImageUpload(file) {
  369. const flag = file.size / 1024 / 1024 < 5
  370. if (!flag) {
  371. this.$message.error('发票图片大小不能超过 5MB!')
  372. }
  373. return flag
  374. },
  375. uploadInvoiceImageSuccess(formItem, { response, file, fileList }) {
  376. formItem.invoiceImageList = fileList
  377. formItem.invoiceImage = response.data
  378. },
  379. handleInvoiceImageRemove(formItem, { file, fileList }) {
  380. formItem.invoiceImageList = fileList
  381. formItem.invoiceImage = ''
  382. },
  383. },
  384. }
  385. </script>
  386. <style lang="scss" scoped>
  387. .club-device {
  388. ::v-deep {
  389. .el-input.is-active .el-input__inner,
  390. .el-input__inner:focus {
  391. @include themify($themes) {
  392. border-color: themed('color');
  393. }
  394. }
  395. }
  396. }
  397. // pc端
  398. @media screen and (min-width: 768px) {
  399. .el-select {
  400. width: 100%;
  401. }
  402. .device-section {
  403. position: relative;
  404. .el-form {
  405. padding-bottom: 10px;
  406. }
  407. .remove-btn {
  408. position: absolute;
  409. right: 0;
  410. bottom: 24px;
  411. font-size: 16px;
  412. color: #f94b4b;
  413. text-decoration: underline;
  414. cursor: pointer;
  415. }
  416. }
  417. .device-param-list {
  418. position: relative;
  419. .add-param {
  420. position: absolute;
  421. cursor: pointer;
  422. top: -40px;
  423. right: 0;
  424. text-decoration: underline;
  425. font-size: 14px;
  426. @include themify($themes) {
  427. color: themed('color');
  428. }
  429. }
  430. .param {
  431. position: relative;
  432. .remove {
  433. position: absolute;
  434. right: 0;
  435. top: 0;
  436. width: 20px;
  437. height: 20px;
  438. background: #f94b4b;
  439. border-radius: 2px;
  440. cursor: pointer;
  441. color: #fff;
  442. font-size: 14px;
  443. text-align: center;
  444. line-height: 20px;
  445. }
  446. }
  447. }
  448. .add-device {
  449. display: flex;
  450. justify-content: center;
  451. align-items: center;
  452. width: 162px;
  453. height: 46px;
  454. border-radius: 4px;
  455. box-sizing: border-box;
  456. font-size: 18px;
  457. margin: 0 auto;
  458. cursor: pointer;
  459. @include themify($themes) {
  460. border: 1px solid themed('color');
  461. color: themed('color');
  462. }
  463. .add-icon {
  464. width: 20px;
  465. height: 20px;
  466. position: relative;
  467. margin-right: 16px;
  468. &::before,
  469. &::after {
  470. position: absolute;
  471. width: 3px;
  472. height: 20px;
  473. left: 50%;
  474. top: 50%;
  475. transform: translate(-50%, -50%);
  476. border-radius: 1px;
  477. content: '';
  478. display: block;
  479. @include themify($themes) {
  480. background: themed('color');
  481. }
  482. }
  483. &::after {
  484. transform: translate(-50%, -50%) rotateZ(90deg);
  485. }
  486. }
  487. }
  488. }
  489. // 移动端
  490. @media screen and (max-width: 768px) {
  491. ::v-deep {
  492. .el-form-item__label {
  493. font-size: 3.4vw;
  494. }
  495. }
  496. .el-select {
  497. width: 100%;
  498. }
  499. .device-param-list {
  500. position: relative;
  501. .add-param {
  502. position: absolute;
  503. cursor: pointer;
  504. top: -40px;
  505. right: 0;
  506. font-size: 3.4vw;
  507. @include themify($themes) {
  508. color: themed('color');
  509. }
  510. }
  511. .param {
  512. position: relative;
  513. .remove {
  514. position: absolute;
  515. right: 0;
  516. top: 0;
  517. width: 4.4vw;
  518. height: 4.4vw;
  519. background: #f94b4b;
  520. border-radius: 0.2vw;
  521. cursor: pointer;
  522. color: #fff;
  523. font-size: 3.4vw;
  524. text-align: center;
  525. line-height: 4.4vw;
  526. }
  527. }
  528. }
  529. .add-device {
  530. display: flex;
  531. justify-content: center;
  532. align-items: center;
  533. width: 31vw;
  534. height: 8.8vw;
  535. border-radius: 0.4vw;
  536. box-sizing: border-box;
  537. font-size: 3.4vw;
  538. margin: 0 auto;
  539. cursor: pointer;
  540. @include themify($themes) {
  541. border: 1px solid themed('color');
  542. color: themed('color');
  543. }
  544. .add-icon {
  545. width: 20px;
  546. height: 20px;
  547. position: relative;
  548. margin-right: 16px;
  549. &::before,
  550. &::after {
  551. position: absolute;
  552. width: 0.6vw;
  553. height: 4.1vw;
  554. left: 50%;
  555. top: 50%;
  556. transform: translate(-50%, -50%);
  557. border-radius: 1px;
  558. content: '';
  559. display: block;
  560. @include themify($themes) {
  561. background: themed('color');
  562. }
  563. }
  564. &::after {
  565. transform: translate(-50%, -50%) rotateZ(90deg);
  566. }
  567. }
  568. }
  569. }
  570. </style>