ImportSecgroupRuleDialog.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <template>
  2. <base-dialog @cancel="cancelDialog">
  3. <div slot="header">{{$t('compute.import_secgroup_rule', [])}}</div>
  4. <div slot="body">
  5. <a-spin :spinning="loading">
  6. <a-form
  7. :form="form.fc">
  8. <a-alert type="warning" class="mb-2">
  9. <template v-slot:message>
  10. <div class="messages-list">
  11. <p>{{ $t('system.text_501') }}<a-button type="link" @click="handleDownloadTemplate">{{$t('system.text_502')}}</a-button></p>
  12. </div>
  13. </template>
  14. </a-alert>
  15. <div class="pt-3 pb-3">
  16. <a-form-item>
  17. <a-upload-dragger
  18. v-decorator="decorators.fileList"
  19. :beforeUpload="beforeUpload"
  20. :fileList="fileList"
  21. :accept="accept"
  22. :remove="handleRemove">
  23. <div class="pt-3 pb-3">
  24. <p class="ant-upload-drag-icon"><a-icon type="inbox" /></p>
  25. <p class="ant-upload-text">{{$t('system.text_505')}}</p>
  26. <p class="ant-upload-hint">{{$t('system.text_506')}}</p>
  27. </div>
  28. </a-upload-dragger>
  29. </a-form-item>
  30. </div>
  31. </a-form>
  32. </a-spin>
  33. </div>
  34. <div slot="footer">
  35. <a-button type="primary" @click="handleConfirm" :loading="loading">{{ $t('dialog.ok') }}</a-button>
  36. <a-button @click="cancelDialog">{{ $t('dialog.cancel') }}</a-button>
  37. </div>
  38. </base-dialog>
  39. </template>
  40. <script>
  41. import XLSX from 'xlsx'
  42. import DialogMixin from '@/mixins/dialog'
  43. import WindowsMixin from '@/mixins/windows'
  44. import { download } from '@/utils/utils'
  45. export default {
  46. name: 'ImportSecgroupRuleDialog',
  47. mixins: [DialogMixin, WindowsMixin],
  48. data () {
  49. return {
  50. accept: '.xlsx',
  51. fileList: [],
  52. rules: [],
  53. loading: false,
  54. form: {
  55. fc: this.$form.createForm(this),
  56. },
  57. decorators: {
  58. fileList: ['fileList', {
  59. rules: [
  60. { required: true, message: this.$t('system.text_507') },
  61. ],
  62. }],
  63. },
  64. }
  65. },
  66. methods: {
  67. beforeUpload (file) {
  68. this.readWorkbookFromLocalFile(file, (workbook) => {
  69. var first_worksheet = workbook.Sheets[workbook.SheetNames[0]]
  70. var sheetArr = XLSX.utils.sheet_to_json(first_worksheet, { header: 1 })
  71. const converVal = (v) => { return v || '' }
  72. const rules = []
  73. if (sheetArr) {
  74. for (let i = 1; i < sheetArr.length; i++) {
  75. const obj = sheetArr[i]
  76. rules.push({
  77. direction: converVal(obj[3]),
  78. action: converVal(obj[4]),
  79. protocol: converVal(obj[5]),
  80. ports: converVal(obj[6]),
  81. priority: converVal(obj[7]),
  82. cidr: converVal(obj[8]),
  83. })
  84. }
  85. }
  86. this.rules = rules
  87. })
  88. this.fileList = [file]
  89. return false
  90. },
  91. readWorkbookFromLocalFile (file, callback) {
  92. var reader = new FileReader()
  93. reader.onload = function (e) {
  94. var data = e.target.result
  95. var workbook = XLSX.read(data, { type: 'binary' })
  96. if (callback) callback(workbook)
  97. }
  98. reader.readAsBinaryString(file)
  99. },
  100. handleClearFile () {
  101. this.fileList = []
  102. },
  103. handleRemove () {
  104. this.handleClearFile()
  105. },
  106. async handleDownloadTemplate () {
  107. const exportDataOptions = this.params.exportDataOptions
  108. const items = exportDataOptions.items.map(v => {
  109. if (['id', 'secgroup', 'secgroup_id', 'tenant', 'user_tags'].includes(v.key)) {
  110. return {
  111. ...v,
  112. label: `${v.label}(${this.$t('compute.no_required')})`,
  113. }
  114. }
  115. return v
  116. })
  117. const params = {
  118. export: 'xls',
  119. export_keys: items.map(v => v.key).join(','),
  120. export_texts: items.map(v => v.label).join(','),
  121. export_limit: 1,
  122. scope: this.$store.getters.scope,
  123. show_fail_reason: true,
  124. details: true,
  125. }
  126. const response = await this.$http({
  127. methods: 'GET',
  128. url: `/v2/${exportDataOptions.resource}`,
  129. params,
  130. responseType: 'blob',
  131. headers: {
  132. 'X-Export-Keys': true,
  133. },
  134. })
  135. const contentDisposition = response.headers['content-disposition']
  136. let fileName = 'unknown'
  137. if (contentDisposition) {
  138. const fileNameMatch = contentDisposition.match(/filename="(.+)"/)
  139. if (fileNameMatch.length === 2) fileName = fileNameMatch[1]
  140. }
  141. download(response.data, fileName, response.headers['content-type'])
  142. },
  143. async handleConfirm () {
  144. try {
  145. if (!this.fileList || this.fileList.length === 0) {
  146. this.form.fc.setFieldsValue({
  147. fileList: undefined,
  148. })
  149. }
  150. this.loading = true
  151. await this.form.fc.validateFields()
  152. await new this.$Manager('secgroups').performAction({
  153. id: this.params.data[0].id,
  154. action: 'import-rules',
  155. data: { rules: this.rules },
  156. })
  157. this.params.refresh()
  158. this.cancelDialog()
  159. this.$message.success(this.$t('compute.import_secgroup_rule', [this.$t('common_159')]))
  160. } catch (err) {
  161. throw err
  162. } finally {
  163. this.loading = false
  164. }
  165. },
  166. },
  167. }
  168. </script>
  169. <style lang="less" scoped>
  170. .messages-list p{
  171. margin-bottom: 2px;
  172. }
  173. </style>