BackupCreate.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. <template>
  2. <base-dialog @cancel="cancelDialog">
  3. <div slot="header">{{$t('compute.create_disk_backup')}}</div>
  4. <div slot="body">
  5. <dialog-selected-tips :name="$t('dictionary.server')" :count="params.data.length" :action="$t('compute.create_disk_backup')" />
  6. <dialog-table
  7. :data="params.data"
  8. :columns="params.columns.slice(0, 3)" />
  9. <a-form :form="form.fc" v-bind="formItemLayout">
  10. <a-form-item :label="$t('compute.backup_type')">
  11. <a-radio-group
  12. v-decorator="decorators.type"
  13. @change="typeChangeHandle">
  14. <a-radio value="disk" :disabled="isEsxi">{{$t('compute.disk_backup')}}</a-radio>
  15. <a-radio value="instance">{{$t('compute.instance_backup')}}</a-radio>
  16. </a-radio-group>
  17. </a-form-item>
  18. <a-form-item
  19. :label="$t('compute.text_1254')"
  20. v-if="isDiskBackup">
  21. <base-select
  22. style="width: 100%"
  23. v-decorator="decorators.disk"
  24. :params="diskParams"
  25. :select-props="{ placeholder: $t('compute.text_1085') }"
  26. resource="disks"
  27. idKey="disk_id"
  28. nameKey="disk"
  29. :label-format="getDiskLabel"
  30. :ctx="[['servers', this.params.data[0].id]]"
  31. :filterable="true"
  32. :isDefaultSelect="true"
  33. :item.sync="selectDisk" />
  34. </a-form-item>
  35. <a-form-item
  36. :label="$t('compute.backup_name')"
  37. class="mb-0">
  38. <a-input
  39. v-decorator="decorators.name"
  40. :placeholder="$t('validator.resourceName')" />
  41. <div slot="extra">
  42. <div v-if="showRepeatTips">{{$t('compute.text_1091')}}</div>
  43. </div>
  44. </a-form-item>
  45. <a-form-item
  46. :label="$t('compute.backup_storage')">
  47. <base-select
  48. style="width: 100%"
  49. v-decorator="decorators.storage"
  50. :params="storageParams"
  51. :select-props="{ placeholder: $t('compute.text_1022', [$t('compute.backup_storage')]) }"
  52. resource="backupstorages"
  53. :filterable="true"
  54. :isDefaultSelect="true" />
  55. </a-form-item>
  56. </a-form>
  57. </div>
  58. <div slot="footer">
  59. <a-button type="primary" @click="handleConfirm" :loading="loading" :disabled="disabledSubmit">{{
  60. $t("dialog.ok")
  61. }}</a-button>
  62. <a-button @click="cancelDialog">{{ $t("dialog.cancel") }}</a-button>
  63. </div>
  64. </base-dialog>
  65. </template>
  66. <script>
  67. import debounce from 'lodash/debounce'
  68. import * as R from 'ramda'
  69. import { mapGetters } from 'vuex'
  70. import { DISK_TYPE } from '@Compute/constants'
  71. import { INPUT_DEBOUNCE_TIMER } from '@/constants/config'
  72. import DialogMixin from '@/mixins/dialog'
  73. import WindowsMixin from '@/mixins/windows'
  74. import { sizestr } from '@/utils/utils'
  75. import { typeClouds } from '@/utils/common/hypervisor'
  76. const hypervisorMap = typeClouds.hypervisorMap
  77. export default {
  78. name: 'VmBackupCreateDialog',
  79. mixins: [DialogMixin, WindowsMixin],
  80. data () {
  81. return {
  82. loading: false,
  83. showRepeatTips: false,
  84. form: {
  85. fc: this.$form.createForm(this, {
  86. name: 'backup_create_form',
  87. onFieldsChange: this.onFieldsChange,
  88. onValuesChange: this.onValuesChange,
  89. }),
  90. fd: {
  91. name: '',
  92. type: this.params.data[0].hypervisor === hypervisorMap.esxi.key ? 'instance' : 'disk',
  93. },
  94. },
  95. decorators: {
  96. type: [
  97. 'type',
  98. {
  99. initialValue: this.params.data[0].hypervisor === hypervisorMap.esxi.key ? 'instance' : 'disk',
  100. },
  101. ],
  102. disk: [
  103. 'disk',
  104. {
  105. rules: [
  106. { required: true, message: this.$t('compute.text_1085'), trigger: 'change' },
  107. ],
  108. },
  109. ],
  110. name: [
  111. 'name',
  112. {
  113. validateFirst: true,
  114. rules: [
  115. { required: true, message: this.$t('compute.text_1256') },
  116. // { validator: this.$validate('resourceName') },
  117. ],
  118. },
  119. ],
  120. storage: [
  121. 'storage',
  122. {
  123. rules: [
  124. { required: true, message: this.$t('compute.text_1022', [this.$t('compute.backup_storage')]), trigger: 'change' },
  125. ],
  126. },
  127. ],
  128. },
  129. formItemLayout: {
  130. labelCol: { span: 4 },
  131. wrapperCol: { span: 20 },
  132. },
  133. selectDisk: {},
  134. disabledSubmit: false,
  135. }
  136. },
  137. computed: {
  138. ...mapGetters(['scope']),
  139. diskParams () {
  140. return {
  141. scope: this.$store.getters.scope,
  142. details: true,
  143. with_meta: true,
  144. }
  145. },
  146. isDiskBackup () {
  147. return this.form.fd.type === 'disk'
  148. },
  149. manager () {
  150. return new this.$Manager(this.isDiskBackup ? 'diskbackups' : 'instancebackups', 'v2')
  151. },
  152. storageParams () {
  153. const params = {
  154. scope: this.scope,
  155. project_domain: this.params.data[0].project_domain,
  156. }
  157. if (this.isDiskBackup) {
  158. params.disk_id = this.selectDisk.id
  159. } else {
  160. params.server_id = this.params.data[0].id
  161. }
  162. return params
  163. },
  164. },
  165. watch: {
  166. 'form.fd.name' (val) {
  167. this.debounceCheckTemplateName()
  168. },
  169. 'selectDisk' (val) {
  170. this.disabledSubmit = val.storage_type === 'nova'
  171. },
  172. },
  173. created () {
  174. this.debounceCheckTemplateName = debounce(() => {
  175. this.checkTemplateName()
  176. }, INPUT_DEBOUNCE_TIMER)
  177. },
  178. methods: {
  179. checkTemplateName () {
  180. const name = this.form.fd.name
  181. if (!R.isNil(name) && !R.isEmpty(name)) {
  182. this.manager.get({ id: name, params: { scope: this.$store.getters.scope } })
  183. .then(res => {
  184. const data = res.data
  185. if (!R.isNil(data) && !R.isEmpty(data)) {
  186. this.showRepeatTips = true // 重复名字
  187. }
  188. }).catch(() => {
  189. this.showRepeatTips = false
  190. })
  191. } else {
  192. this.showRepeatTips = false
  193. }
  194. },
  195. validateForm (fileds = []) {
  196. return new Promise((resolve, reject) => {
  197. this.form.fc.validateFields(fileds, (err, values) => {
  198. if (!err) {
  199. resolve(values)
  200. } else {
  201. reject(err)
  202. }
  203. })
  204. })
  205. },
  206. async doCreateDiskBackup () {
  207. const values = await this.validateForm(['disk', 'name', 'storage'])
  208. const params = {
  209. disk_id: values.disk,
  210. generate_name: values.name,
  211. backup_storage_id: values.storage,
  212. }
  213. return this.manager.create({ data: params })
  214. },
  215. async doCreateInstanceBackup () {
  216. const values = await this.validateForm(['name', 'storage'])
  217. const params = {
  218. generate_name: values.name,
  219. backup_storage_id: values.storage,
  220. }
  221. return this.params.onManager('performAction', {
  222. id: this.params.data[0].id,
  223. steadyStatus: ['running', 'ready'],
  224. managerArgs: {
  225. action: 'instance-backup',
  226. data: params,
  227. },
  228. })
  229. },
  230. async handleConfirm () {
  231. this.loading = true
  232. try {
  233. if (this.isDiskBackup) {
  234. await this.doCreateDiskBackup()
  235. } else {
  236. await this.doCreateInstanceBackup()
  237. }
  238. this.loading = false
  239. this.params.refresh()
  240. this.cancelDialog()
  241. this.$message.success(this.$t('compute.text_322'))
  242. } catch (error) {
  243. this.loading = false
  244. this.cancelDialog()
  245. }
  246. },
  247. getDiskLabel (item) {
  248. return `${item.disk}(${
  249. item.disk_type === DISK_TYPE.sys.value
  250. ? DISK_TYPE.sys.text
  251. : DISK_TYPE.data.text
  252. }, ${sizestr(item.disk_size, 'M', 1024)})`
  253. },
  254. typeChangeHandle (e) {
  255. this.$nextTick(() => {
  256. this.debounceCheckTemplateName()
  257. })
  258. },
  259. onValuesChange (props, values) {
  260. Object.keys(values).forEach((key) => {
  261. this.form.fd[key] = values[key]
  262. })
  263. },
  264. },
  265. }
  266. </script>