form-club-device.vue 16 KB

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