form-club-device.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  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">
  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. }
  196. },
  197. computed: {
  198. ...mapGetters(['authUserId']),
  199. },
  200. created() {
  201. this.fetchBrandList()
  202. this.fetchDeviceList()
  203. this.initFormList()
  204. },
  205. methods: {
  206. // 表单验证
  207. validate() {
  208. this.$emit('step', this.formatFormList())
  209. return Promise.all(this.$refs.form.map((item) => item.validate()))
  210. },
  211. async init(formData) {
  212. console.log('formData', formData)
  213. const obj = {}
  214. const productImageList = [
  215. {
  216. name: '',
  217. url: formData?.productImage,
  218. },
  219. ]
  220. const invoiceImageList = [
  221. {
  222. name: '',
  223. url: formData.invoiceImage,
  224. },
  225. ]
  226. obj.uuid = ++this.uuid
  227. obj.productImageList = productImageList
  228. obj.invoiceImageList = invoiceImageList
  229. obj.productImage = formData.productImage
  230. obj.productName = formData.productName
  231. obj.snCode = formData.snCode
  232. obj.brandId = formData.brandId
  233. obj.productId = formData.productId
  234. obj.productTypeId = formData.productTypeId
  235. obj.purchaseWay = formData.purchaseWay
  236. obj.invoiceImage = formData.invoiceImage
  237. obj.paramList = formData.paramList
  238. this.formList.splice(0, 1, obj)
  239. console.log('formList', this.formList)
  240. },
  241. formatFormList() {
  242. const list = []
  243. this.formList.forEach((formItem) => {
  244. const obj = {}
  245. obj.productImage = formItem.productImage
  246. obj.productName = formItem.productName
  247. obj.snCode = formItem.snCode
  248. obj.brandId = formItem.brandId
  249. obj.productId = formItem.productId
  250. obj.source = 2
  251. obj.productTypeId = formItem.productTypeId
  252. obj.purchaseWay = formItem.purchaseWay
  253. obj.invoiceImage = formItem.invoiceImage
  254. obj.paramList = formItem.paramList
  255. list.push(obj)
  256. })
  257. return list
  258. },
  259. generateFormData() {
  260. return {
  261. uuid: ++this.uuid,
  262. authUserId: '',
  263. authId: '', // 授权id
  264. createBy: '', // 创建人id
  265. // 设备参数列表
  266. paramList: this.initParams(),
  267. productId: '', // 授权设备id
  268. productImage: '', // 设备图片
  269. productName: '', // 设备名称
  270. snCode: '', // 设备SN码
  271. brandId: '',
  272. productTypeId: '',
  273. purchaseWay: '', // 购买渠道
  274. invoiceImage: '', // 发票
  275. productImageList: [],
  276. invoiceImageList: [],
  277. }
  278. },
  279. generageProductParam() {
  280. return {
  281. paramContent: '',
  282. paramName: '',
  283. }
  284. },
  285. initParams() {
  286. const list = []
  287. for (let i = 0; i < 4; i++) {
  288. list.push(this.generageProductParam())
  289. }
  290. return list
  291. },
  292. insertParam(formItem) {
  293. formItem.paramList.push(this.generateFormData())
  294. },
  295. removeParam(formItem, index) {
  296. formItem.paramList.splice(index, 1)
  297. },
  298. initFormList() {
  299. this.formList.push(this.generateFormData())
  300. console.log(this.formList)
  301. },
  302. insertOne() {
  303. this.formList.push(this.generateFormData())
  304. },
  305. removeOne(formItem) {
  306. const index = this.formList.findIndex(
  307. (item) => item.uuid === formItem.uuid
  308. )
  309. this.formList.splice(index, 1)
  310. },
  311. onProductNameChange(formItem, value) {
  312. if (typeof value === 'number') {
  313. formItem.productTypeId = value
  314. const deviceInfo = this.deviceList.find(
  315. (item) => item.productTypeId === value
  316. )
  317. formItem.productImage = deviceInfo.image
  318. formItem.productImageList = [{ name: '', url: deviceInfo.image }]
  319. } else {
  320. formItem.productTypeId = ''
  321. formItem.productImage = ''
  322. formItem.productImageList = []
  323. }
  324. },
  325. // 获取品牌列表
  326. async fetchBrandList() {
  327. try {
  328. const res = await this.$http.api.fetchBrandList({
  329. type: 3,
  330. authUserId: this.authUserId,
  331. })
  332. this.brandList = res.data
  333. } catch (error) {
  334. console.log(error)
  335. }
  336. },
  337. // 获取设备列表
  338. async fetchDeviceList() {
  339. try {
  340. const res = await this.$http.api.fetchProductSelectList({
  341. authUserId: this.authUserId,
  342. })
  343. this.deviceList = res.data
  344. } catch (error) {
  345. console.log(error)
  346. }
  347. },
  348. // 产品图片上传
  349. beforeProductImageUpload(file) {
  350. const flag = file.size / 1024 / 1024 < 5
  351. if (!flag) {
  352. this.$message.error('上传产品图片大小不能超过 5MB!')
  353. }
  354. return flag
  355. },
  356. uploadProductImageSuccess(formItem, { response, file, fileList }) {
  357. formItem.productImageList = fileList
  358. formItem.productImage = response.data
  359. },
  360. handleProductImageRemove(formItem, { file, fileList }) {
  361. formItem.productImageList = fileList
  362. formItem.productImage = ''
  363. },
  364. // 发票上传
  365. beforeInvoiceImageUpload(file) {
  366. const flag = file.size / 1024 / 1024 < 5
  367. if (!flag) {
  368. this.$message.error('发票图片大小不能超过 5MB!')
  369. }
  370. return flag
  371. },
  372. uploadInvoiceImageSuccess(formItem, { response, file, fileList }) {
  373. formItem.invoiceImageList = fileList
  374. formItem.invoiceImage = response.data
  375. },
  376. handleInvoiceImageRemove(formItem, { file, fileList }) {
  377. formItem.invoiceImageList = fileList
  378. formItem.invoiceImage = ''
  379. },
  380. },
  381. }
  382. </script>
  383. <style lang="scss" scoped>
  384. .club-device {
  385. ::v-deep {
  386. .el-input.is-active .el-input__inner,
  387. .el-input__inner:focus {
  388. @include themify($themes) {
  389. border-color: themed('color');
  390. }
  391. }
  392. }
  393. }
  394. // pc端
  395. @media screen and (min-width: 768px) {
  396. .el-select {
  397. width: 100%;
  398. }
  399. .device-section {
  400. position: relative;
  401. .el-form {
  402. padding-bottom: 10px;
  403. }
  404. .remove-btn {
  405. position: absolute;
  406. right: 0;
  407. bottom: 24px;
  408. font-size: 16px;
  409. color: #f94b4b;
  410. text-decoration: underline;
  411. cursor: pointer;
  412. }
  413. }
  414. .device-param-list {
  415. position: relative;
  416. .add-param {
  417. position: absolute;
  418. cursor: pointer;
  419. top: -40px;
  420. right: 0;
  421. text-decoration: underline;
  422. font-size: 14px;
  423. @include themify($themes) {
  424. color: themed('color');
  425. }
  426. }
  427. .param {
  428. position: relative;
  429. .remove {
  430. position: absolute;
  431. right: 0;
  432. top: 0;
  433. width: 20px;
  434. height: 20px;
  435. background: #f94b4b;
  436. border-radius: 2px;
  437. cursor: pointer;
  438. color: #fff;
  439. font-size: 14px;
  440. text-align: center;
  441. line-height: 20px;
  442. }
  443. }
  444. }
  445. .add-device {
  446. display: flex;
  447. justify-content: center;
  448. align-items: center;
  449. width: 162px;
  450. height: 46px;
  451. border-radius: 4px;
  452. box-sizing: border-box;
  453. font-size: 18px;
  454. margin: 0 auto;
  455. cursor: pointer;
  456. @include themify($themes) {
  457. border: 1px solid themed('color');
  458. color: themed('color');
  459. }
  460. .add-icon {
  461. width: 20px;
  462. height: 20px;
  463. position: relative;
  464. margin-right: 16px;
  465. &::before,
  466. &::after {
  467. position: absolute;
  468. width: 3px;
  469. height: 20px;
  470. left: 50%;
  471. top: 50%;
  472. transform: translate(-50%, -50%);
  473. border-radius: 1px;
  474. content: '';
  475. display: block;
  476. @include themify($themes) {
  477. background: themed('color');
  478. }
  479. }
  480. &::after {
  481. transform: translate(-50%, -50%) rotateZ(90deg);
  482. }
  483. }
  484. }
  485. }
  486. // 移动端
  487. @media screen and (max-width: 768px) {
  488. ::v-deep {
  489. .el-form-item__label {
  490. font-size: 3.4vw;
  491. }
  492. }
  493. .el-select {
  494. width: 100%;
  495. }
  496. .device-param-list {
  497. position: relative;
  498. .add-param {
  499. position: absolute;
  500. cursor: pointer;
  501. top: -40px;
  502. right: 0;
  503. font-size: 3.4vw;
  504. @include themify($themes) {
  505. color: themed('color');
  506. }
  507. }
  508. .param {
  509. position: relative;
  510. .remove {
  511. position: absolute;
  512. right: 0;
  513. top: 0;
  514. width: 4.4vw;
  515. height: 4.4vw;
  516. background: #f94b4b;
  517. border-radius: 0.2vw;
  518. cursor: pointer;
  519. color: #fff;
  520. font-size: 3.4vw;
  521. text-align: center;
  522. line-height: 4.4vw;
  523. }
  524. }
  525. }
  526. .add-device {
  527. display: flex;
  528. justify-content: center;
  529. align-items: center;
  530. width: 31vw;
  531. height: 8.8vw;
  532. border-radius: 0.4vw;
  533. box-sizing: border-box;
  534. font-size: 3.4vw;
  535. margin: 0 auto;
  536. cursor: pointer;
  537. @include themify($themes) {
  538. border: 1px solid themed('color');
  539. color: themed('color');
  540. }
  541. .add-icon {
  542. width: 20px;
  543. height: 20px;
  544. position: relative;
  545. margin-right: 16px;
  546. &::before,
  547. &::after {
  548. position: absolute;
  549. width: 0.6vw;
  550. height: 4.1vw;
  551. left: 50%;
  552. top: 50%;
  553. transform: translate(-50%, -50%);
  554. border-radius: 1px;
  555. content: '';
  556. display: block;
  557. @include themify($themes) {
  558. background: themed('color');
  559. }
  560. }
  561. &::after {
  562. transform: translate(-50%, -50%) rotateZ(90deg);
  563. }
  564. }
  565. }
  566. }
  567. </style>