Device.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <template>
  2. <schema-table
  3. ref="table"
  4. row-key="id"
  5. :schema="schema"
  6. @row-click="onRowClick"
  7. >
  8. <confirm-dialog
  9. ref="editDialog"
  10. :title="dialogTitle"
  11. @confirm="onSave"
  12. >
  13. <div class="c-grid-form u-align-self--center">
  14. <span class="c-grid-form__label u-required">名称</span>
  15. <el-input
  16. v-model.trim="currObj.name"
  17. placeholder="最多50个字符"
  18. maxlength="50"
  19. clearable
  20. />
  21. <span class="c-grid-form__label u-required">配置</span>
  22. <schema-select
  23. v-model="currObj.productId"
  24. placeholder="请选择配置"
  25. :schema="productSelectSchema"
  26. :disabled="isSub"
  27. />
  28. <span class="c-grid-form__label u-required">序列号</span>
  29. <el-input
  30. v-model.trim="currObj.serialNumber"
  31. placeholder="最多50个字符"
  32. maxlength="50"
  33. clearable
  34. />
  35. <span class="c-grid-form__label u-required">MAC</span>
  36. <el-input
  37. v-model.trim="currObj.mac"
  38. placeholder="ff:ff:ff:ff:ff:ff"
  39. maxlength="17"
  40. clearable
  41. />
  42. <span class="c-grid-form__label u-required">地址</span>
  43. <el-input
  44. v-model.trim="currObj.address"
  45. placeholder="最多100个字符"
  46. maxlength="100"
  47. clearable
  48. />
  49. <span class="c-grid-form__label">坐标</span>
  50. <div class="l-flex--row c-grid-form__option">
  51. <span class="c-sibling-item">经度</span>
  52. <el-input
  53. v-model.trim="currObj.longitude"
  54. class="l-flex__fill c-sibling-item"
  55. placeholder="-180 ~ +180"
  56. maxlength="13"
  57. clearable
  58. />
  59. <span class="c-sibling-item far c-grid-form__label">纬度</span>
  60. <el-input
  61. v-model.trim="currObj.latitude"
  62. class="l-flex__fill c-sibling-item"
  63. placeholder="-90 ~ +90"
  64. maxlength="12"
  65. clearable
  66. />
  67. </div>
  68. <span />
  69. <a
  70. class="o-link grid"
  71. href="http://api.map.baidu.com/lbsapi/getpoint/index.html"
  72. target="_blank"
  73. />
  74. </div>
  75. </confirm-dialog>
  76. </schema-table>
  77. </template>
  78. <script>
  79. import {
  80. getDevicesByAdmin,
  81. addDevice,
  82. deleteDevice,
  83. getSubDevices,
  84. addSubDevice,
  85. activateDevice,
  86. deactivateDevice,
  87. getProducts
  88. } from '@/api/device'
  89. import {
  90. validMAC,
  91. validLongitude,
  92. validLatitude
  93. } from '@/utils/validate'
  94. export default {
  95. name: 'Device',
  96. props: {
  97. tenant: {
  98. type: String,
  99. required: true
  100. }
  101. },
  102. data () {
  103. const productSelectSchema = {
  104. remote: this.getProducts,
  105. pagination: true,
  106. value: 'id',
  107. label: 'name'
  108. }
  109. return {
  110. currObj: {},
  111. isSub: false,
  112. ratio: null,
  113. productSelectSchema,
  114. schema: {
  115. keepalive: true,
  116. condition: { productId: void 0, name: '' },
  117. list: this.getDevicesByAdmin,
  118. transform: this.transform,
  119. transformData: __SUB_DEVICE__ && this.transformTableData,
  120. buttons: [
  121. { type: 'add', on: this.onAddDevice }
  122. ],
  123. filters: [
  124. { key: 'productId', type: 'select', placeholder: '全部配置', ...productSelectSchema },
  125. { key: 'name', type: 'search', placeholder: '设备名称' }
  126. ],
  127. cols: [
  128. { type: 'refresh', render: __SUB_DEVICE__
  129. ? (data, h) => data.isMaster
  130. ? h('i', {
  131. staticClass: `o-expand-icon u-pointer ${data.loading ? 'el-icon-loading' : 'el-icon-arrow-right'}`,
  132. class: { expand: data.expand }
  133. })
  134. : null
  135. : null },
  136. __SUB_DEVICE__
  137. ? { label: '设备名称', render: (data, h) => data.empty ? h('span', { staticClass: 'u-color--info' }, '暂无备份设备') : data.name, 'min-width': 120 }
  138. : { prop: 'name', label: '设备名称', 'min-width': 120 },
  139. { prop: 'productName', label: '配置' },
  140. { prop: 'serialNumber', label: '序列号', 'min-width': 140 },
  141. { prop: 'mac', label: 'MAC', 'min-width': 140 },
  142. { type: 'tag', render: ({ empty, activate, onlineStatus }) => empty
  143. ? null
  144. : activate
  145. ? onlineStatus === 0
  146. ? { type: 'primary', label: '已启用' }
  147. : onlineStatus === 1
  148. ? { type: 'success', label: '在线' }
  149. : { type: 'danger', label: '离线' }
  150. : { type: 'warning', label: '未激活' },
  151. on: this.onTagClick },
  152. { type: 'invoke', width: __SUB_DEVICE__ ? 180 : 120, render: [
  153. { label: '配置', render: ({ isMaster }) => isMaster, on: this.onSettingDevice },
  154. __SUB_DEVICE__ ? { label: '添加备份', render: ({ isMaster }) => isMaster, on: this.onAddSubDevice } : null,
  155. { label: '删除', render: ({ empty }) => !empty, on: this.onDelDevice }
  156. ].filter(Boolean) }
  157. ]
  158. }
  159. }
  160. },
  161. computed: {
  162. dialogTitle () {
  163. return this.isSub ? '新增备份设备' : '新增设备'
  164. }
  165. },
  166. watch: {
  167. group () {
  168. this.$refs.table.pageTo(1)
  169. }
  170. },
  171. methods: {
  172. getProducts (params) {
  173. return getProducts({
  174. tenant: this.tenant,
  175. ...params
  176. })
  177. },
  178. getDevicesByAdmin (params) {
  179. return getDevicesByAdmin({
  180. tenant: this.tenant,
  181. ...params
  182. })
  183. },
  184. transform (data) {
  185. return {
  186. ...data,
  187. loaded: false,
  188. loading: false,
  189. expand: false,
  190. subs: [],
  191. isMaster: true
  192. }
  193. },
  194. transformTableData (data) {
  195. const arr = []
  196. data.forEach(item => {
  197. arr.push(item)
  198. if (item.loaded && item.expand) {
  199. arr.push(...item.subs)
  200. }
  201. })
  202. return arr
  203. },
  204. onAddDevice () {
  205. this.isSub = false
  206. this.$master = null
  207. this.productSelectSchema.option = null
  208. this.onAdd()
  209. },
  210. onAddSubDevice (item) {
  211. this.isSub = true
  212. this.$master = item
  213. this.productSelectSchema.option = { value: item.productId, label: item.productName }
  214. this.onAdd()
  215. },
  216. onAdd () {
  217. this.currObj = {
  218. name: '',
  219. productId: this.$master?.productId,
  220. serialNumber: '',
  221. mac: '',
  222. longitude: '',
  223. latitude: '',
  224. address: '',
  225. tenant: this.tenant
  226. }
  227. this.$refs.editDialog.show()
  228. },
  229. onSave (done) {
  230. if (this.check(this.currObj)) {
  231. if (this.isSub) {
  232. addSubDevice(this.$master, this.currObj).then(() => {
  233. done()
  234. this.reloadSubDevices(this.$master)
  235. })
  236. } else {
  237. addDevice(this.currObj).then(() => {
  238. done()
  239. this.$refs.table.resetCondition({ name: this.currObj.name, productId: this.currObj.productId })
  240. })
  241. }
  242. }
  243. },
  244. check (item) {
  245. if (!item.name) {
  246. this.$message({
  247. type: 'warning',
  248. message: '名称不能为空'
  249. })
  250. return false
  251. }
  252. if (!item.productId) {
  253. this.$message({
  254. type: 'warning',
  255. message: '请选择配置'
  256. })
  257. return false
  258. }
  259. if (!item.serialNumber) {
  260. this.$message({
  261. type: 'warning',
  262. message: '序列号不能为空'
  263. })
  264. return false
  265. }
  266. if (!item.mac) {
  267. this.$message({
  268. type: 'warning',
  269. message: 'MAC不能为空'
  270. })
  271. return false
  272. }
  273. if (!validMAC(item.mac)) {
  274. this.$message({
  275. type: 'warning',
  276. message: 'MAC格式不正确,例 ff:ff:ff:ff:ff:ff'
  277. })
  278. return false
  279. }
  280. item.mac = item.mac.toLowerCase()
  281. if (!item.address) {
  282. this.$message({
  283. type: 'warning',
  284. message: '地址不能为空'
  285. })
  286. return false
  287. }
  288. if (item.longitude && !validLongitude(item.longitude)) {
  289. this.$message({
  290. type: 'warning',
  291. message: '经度格式错误,-180 ~ +180'
  292. })
  293. return false
  294. }
  295. if (item.latitude && !validLatitude(item.latitude)) {
  296. this.$message({
  297. type: 'warning',
  298. message: '纬度格式错误,-90 ~ +90'
  299. })
  300. return false
  301. }
  302. return true
  303. },
  304. onViewDevice (item) {
  305. this.$router.push({
  306. name: 'device-management-detail',
  307. params: {
  308. id: item.id
  309. }
  310. })
  311. },
  312. onDelDevice (item) {
  313. if (item.isMaster) {
  314. deleteDevice(item).then(() => {
  315. this.$refs.table.decrease(1)
  316. })
  317. } else {
  318. deleteDevice(item).then(() => {
  319. this.reloadSubDevices(item.parent)
  320. })
  321. }
  322. },
  323. reloadSubDevices (item) {
  324. item.loaded = false
  325. item.children = []
  326. this.getSubDevices(item)
  327. },
  328. getSubDevices (item, pageSize = 999) {
  329. item.loading = true
  330. getSubDevices({
  331. id: item.id,
  332. pageNum: 1,
  333. pageSize
  334. }).then(({ data, totalCount }) => {
  335. if (totalCount > pageSize) {
  336. this.getSubDevices(item, totalCount)
  337. } else {
  338. item.loading = false
  339. item.loaded = true
  340. item.expand = true
  341. if (data.length === 0) {
  342. item.subs = [{ id: `${Math.random()}`, empty: true }]
  343. } else {
  344. item.subs = data.map(device => {
  345. return {
  346. ...device,
  347. isMaster: false,
  348. empty: false,
  349. parent: item
  350. }
  351. })
  352. }
  353. }
  354. }, () => {
  355. item.loading = false
  356. })
  357. },
  358. onRowClick (data) {
  359. if (__SUB_DEVICE__ && data.isMaster) {
  360. if (data.loaded) {
  361. data.expand = !data.expand
  362. } else if (!data.loading) {
  363. this.getSubDevices(data)
  364. }
  365. }
  366. },
  367. onTagClick (data) {
  368. (data.activate ? deactivateDevice : activateDevice)(data).then(() => {
  369. if (data.isMaster) {
  370. this.$refs.table.pageTo()
  371. } else {
  372. this.reloadSubDevices(data.parent)
  373. }
  374. })
  375. },
  376. onSettingDevice ({ id }) {
  377. this.$router.push({
  378. name: 'tenant-device-management-settings',
  379. params: { id }
  380. })
  381. }
  382. }
  383. }
  384. </script>