mixin.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. import * as R from 'ramda'
  2. import _ from 'lodash'
  3. import { SCHED_POLICY_OPTIONS_MAP, SERVER_TYPE, SELECT_IMAGE_KEY_SUFFIX } from '@Compute/constants'
  4. import OsSelect from '@Compute/sections/OsSelect'
  5. import CpuRadio from '@Compute/sections/CpuRadio'
  6. import MemRadio from '@Compute/sections/MemRadio'
  7. import sku from '@Compute/sections/SKU'
  8. import gpu from '@Compute/sections/GPU/index'
  9. import pci from '@Compute/sections/PCI'
  10. import ServerNetwork from '@Compute/sections/ServerNetwork'
  11. import SchedPolicy from '@Compute/sections/SchedPolicy'
  12. import Duration from '@Compute/sections/Duration'
  13. import InstanceGroups from '@Compute/sections/InstanceGroups'
  14. import DataDisk from '@Compute/sections/DataDisk'
  15. import HostName from '@Compute/sections/HostName'
  16. import storage from '@/utils/storage'
  17. import workflowMixin from '@/mixins/workflow'
  18. import { Manager } from '@/utils/manager'
  19. import { isSuccess } from '@/utils/http'
  20. import NameRepeated from '@/sections/NameRepeated'
  21. import CloudregionZone from '@/sections/CloudregionZone'
  22. import DomainProject from '@/sections/DomainProject'
  23. import { getInitialValue } from '@/utils/common/ant'
  24. import { IMAGES_TYPE_MAP } from '@/constants/compute'
  25. import { HYPERVISORS_MAP } from '@/constants'
  26. import i18n from '@/locales'
  27. import { deleteInvalid } from '@/utils/utils'
  28. import Tag from '../components/Tag'
  29. import { Decorator, GenCreateData } from '../../utils/createServer'
  30. import BottomBar from '../components/BottomBar'
  31. const CreateServerForm = {
  32. wrapperCol: {
  33. md: { span: 18 },
  34. xl: { span: 20 },
  35. xxl: { span: 22 },
  36. },
  37. labelCol: {
  38. md: { span: 6 },
  39. xl: { span: 4 },
  40. xxl: { span: 2 },
  41. },
  42. }
  43. export default {
  44. name: 'IDCCreate',
  45. components: {
  46. OsSelect,
  47. CloudregionZone,
  48. BottomBar,
  49. CpuRadio,
  50. MemRadio,
  51. sku,
  52. ServerNetwork,
  53. DataDisk,
  54. gpu,
  55. SchedPolicy,
  56. DomainProject,
  57. Duration,
  58. InstanceGroups,
  59. Tag,
  60. NameRepeated,
  61. HostName,
  62. pci,
  63. },
  64. mixins: [workflowMixin],
  65. props: {
  66. type: {
  67. type: String,
  68. required: true,
  69. validator: val => ['idc', 'private', 'public'].includes(val),
  70. },
  71. },
  72. data () {
  73. const decorators = new Decorator(SERVER_TYPE[this.type]).createDecorators()
  74. const initFd = getInitialValue(decorators)
  75. return {
  76. submiting: false,
  77. errors: {},
  78. formItemLayout: {
  79. wrapperCol: CreateServerForm.wrapperCol,
  80. labelCol: CreateServerForm.labelCol,
  81. },
  82. form: {
  83. fc: this.$form.createForm(this, { onValuesChange: this.onValuesChange }),
  84. fi: { // formInfo 存储着和表单相关的数据
  85. capability: {}, // 可用区下的可用资源
  86. imageMsg: {}, // 当前选中的 image
  87. cpuMem: {}, // cpu 和 内存 的关联关系
  88. createType: SERVER_TYPE[this.type],
  89. dataDiskDisabled: false, // 数据盘是否禁用
  90. sysDiskDisabled: false, // 系统盘是否禁用
  91. cpuDisabled: false,
  92. memDisabled: false,
  93. dataDiskMedium: '',
  94. networkVpcObj: {},
  95. showCpuSockets: false,
  96. cpuSockets: 1,
  97. errPanes: [], // 表单校验错误的tabs
  98. containerPanes: [], // 子组件同步的tabs
  99. },
  100. fd: { ...initFd, os: '' },
  101. },
  102. decorators,
  103. capabilityParams: {}, // 防止 capability 反复调用,这里对当前的接口参数做记录
  104. price: null,
  105. collapseActive: [],
  106. hostNameValidate: {
  107. validateStatus: '',
  108. errorMsg: '',
  109. },
  110. }
  111. },
  112. provide () {
  113. return {
  114. form: this.form,
  115. }
  116. },
  117. computed: {
  118. project_domain () {
  119. return this.form.fd.domain ? this.form.fd.domain.key : this.$store.getters.userInfo.projectDomainId
  120. },
  121. project () {
  122. return this.form.fd.project ? this.form.fd.project.key : this.$store.getters.userInfo.projectId
  123. },
  124. scopeParams () {
  125. if (this.$store.getters.isAdminMode) {
  126. return {
  127. project_domain: this.project_domain,
  128. }
  129. }
  130. return { scope: this.$store.getters.scope }
  131. },
  132. gpuOptions () {
  133. const specs = this.form.fi.capability.specs || {}
  134. const data = specs.isolated_devices || {}
  135. const ret = []
  136. for (const key in data) {
  137. if (data.hasOwnProperty(key)) {
  138. const item = data[key]
  139. if (item.dev_type.startsWith('GPU')) {
  140. ret.push({
  141. ...item,
  142. key: `vendor=${item.vendor}:${item.model}`,
  143. label: `${item.vendor}/${item.model}`,
  144. })
  145. }
  146. }
  147. }
  148. return ret
  149. },
  150. pciDevTypeOptions () {
  151. return (this.form.fi?.capability?.pci_model_types || []).filter(item => item.hypervisor === 'pod')
  152. },
  153. pciOptions () {
  154. const specs = this.form.fi.capability.specs || {}
  155. const data = specs.isolated_devices || {}
  156. const ret = []
  157. for (const key in data) {
  158. if (data.hasOwnProperty(key)) {
  159. const item = data[key]
  160. if (!item.dev_type.startsWith('USB') && item.hypervisor === 'pod') {
  161. ret.push({
  162. ...item,
  163. key: `vendor=${item.vendor}:${item.model}`,
  164. label: `${item.vendor}/${item.model}`,
  165. })
  166. }
  167. }
  168. }
  169. return ret
  170. },
  171. backupDisableds () { // 高可用判断哪些宿主机可用
  172. const ret = []
  173. if (this.form.fd.schedPolicyType === SCHED_POLICY_OPTIONS_MAP.host.key) {
  174. ret.push(this.form.fd.schedPolicyHost)
  175. }
  176. if (this.storageHostParams && this.storageHostParams.storageHosts && this.storageHostParams.storageHosts.length) {
  177. this.storageHostParams.storageHosts.map(item => {
  178. ret.push(item.id)
  179. })
  180. }
  181. return ret
  182. },
  183. policyHostDisabled () {
  184. if (this.form.fd.backupEnable) {
  185. return [this.form.fd.backup]
  186. }
  187. return []
  188. },
  189. dataDiskSizes () {
  190. const disk = this.form.fd.dataDiskSizes
  191. return R.is(Object, disk) ? Object.values(disk) : []
  192. },
  193. secgroupParams () {
  194. const params = {
  195. ...this.scopeParams,
  196. }
  197. if (this.type === 'public') { // 公有云
  198. if (R.is(Object, this.form.fd.sku)) {
  199. const cloudregion = this.form.fd.sku.cloudregion_id // 取 sku
  200. if (cloudregion) params.cloudregion_id = cloudregion
  201. }
  202. } else { // 私有云和IDC取 CloudregionZone 组件
  203. const cloudregion = _.get(this.form.fd, 'cloudregion.key')
  204. if (cloudregion) params.cloudregion_id = cloudregion
  205. }
  206. if (this.form.fi.networkVpcObj && this.form.fi.networkVpcObj.id) {
  207. params.vpc_id = this.form.fi.networkVpcObj.id
  208. delete params.cloudregion_id
  209. }
  210. return params
  211. },
  212. showSecgroupBind () {
  213. return this.form.fd.networkType === 'manual'
  214. },
  215. isHostImageType () { // 镜像类型为主机镜像
  216. return this.form.fd.imageType === IMAGES_TYPE_MAP.host.key
  217. },
  218. isSnapshotImageType () { // 镜像类型为主机快照
  219. return this.form.fd.imageType === IMAGES_TYPE_MAP.snapshot.key
  220. },
  221. isDomainMode () {
  222. return this.$store.getters.isDomainMode
  223. },
  224. hasMeterService () { // 是否有计费的服务
  225. const { services } = this.$store.getters.userInfo
  226. const meterService = services.find(val => val.type === 'meter')
  227. if (meterService && meterService.status === true) {
  228. return true
  229. }
  230. return false
  231. },
  232. cloudregionZoneParams () {
  233. const params = {}
  234. if (this.type === 'public') { // 公有云
  235. if (R.is(Object, this.form.fd.sku)) {
  236. const cloudregion = this.form.fd.sku.cloudregion_id // 取 sku
  237. const zone = this.form.fd.zone // 取 areaSelect 组件
  238. if (cloudregion) params.cloudregion = cloudregion
  239. if (zone) params.zone = zone
  240. }
  241. } else { // 私有云和IDC取 CloudregionZone 组件
  242. const cloudregion = _.get(this.form.fd, 'cloudregion.key')
  243. const zone = _.get(this.form.fd, 'zone.key')
  244. if (cloudregion) params.cloudregion = cloudregion
  245. if (zone) params.zone = zone
  246. }
  247. return params
  248. },
  249. networkVpcParams () {
  250. const zone = _.get(this.form.fd, 'zone.key')
  251. const params = {
  252. limit: 0,
  253. manager_id: this.form.fd.cloudprovider,
  254. ...this.scopeParams,
  255. }
  256. if (zone) {
  257. params.usable = true
  258. params.zone_id = zone
  259. }
  260. return params
  261. },
  262. vpcResource () {
  263. if (R.is(String, this.cloudregionZoneParams.cloudregion)) return `cloudregions/${this.cloudregionZoneParams.cloudregion}/vpcs`
  264. return ''
  265. },
  266. schedtagParams () { // 网络里指定调度标签
  267. return {
  268. limit: 0,
  269. resource_type: 'networks',
  270. ...this.scopeParams,
  271. }
  272. },
  273. policySchedtagParams () { // 高级配置里面调度策略选择 指定调度标签
  274. const ret = {
  275. limit: 0,
  276. 'filter.0': 'resource_type.equals(hosts)',
  277. ...this.scopeParams,
  278. }
  279. const zone = _.get(this.form.fd, 'zone.key')
  280. if (zone) {
  281. ret.zone_id = zone
  282. }
  283. return ret
  284. },
  285. isWindows () {
  286. let isWindows = false
  287. const osType = (_.get(this.form.fi, 'imageMsg.info.properties.os_type') || _.get(this.form.fi, 'imageMsg.properties.os_type') || '').toLowerCase()
  288. const os = (_.get(this.form.fd, 'os') || '').toLowerCase()
  289. if (~[osType, os].indexOf('windows')) {
  290. isWindows = true
  291. }
  292. return isWindows
  293. },
  294. osType () {
  295. let os_type = this.form.fi.imageMsg.info ? this.form.fi.imageMsg.info.properties?.os_type : this.form.fi.imageMsg.properties?.os_type
  296. if (!os_type && this.form.fi.imageMsg.os_type) {
  297. os_type = this.form.fi.imageMsg.os_type
  298. }
  299. return this.isWindows ? 'windows' : os_type?.toLowerCase()
  300. },
  301. enableEip () {
  302. const externalAccessMode = _.get(this.form.fi, 'networkVpcObj.external_access_mode')
  303. if (externalAccessMode === 'none') return false // "eip-distgw" "eip" 是正常可以使用EIP的,"none"不可以
  304. return true
  305. },
  306. isZStack () {
  307. return this.form.fd.hypervisor === HYPERVISORS_MAP.zstack.key
  308. },
  309. isInCloudSphere () {
  310. return this.form.fd.hypervisor === HYPERVISORS_MAP.incloudsphere.key
  311. },
  312. hostNameTips () {
  313. if (this.isWindows) {
  314. return `${this.$t('compute.host_name_tips')} ${this.$t('compute.validate.windows')}`
  315. } else {
  316. return `${this.$t('compute.host_name_tips')} ${this.$t('compute.validate.others')}`
  317. }
  318. },
  319. isOpenSourceVersion () {
  320. return !this.$appConfig.isPrivate
  321. },
  322. },
  323. created () {
  324. this.zoneM = new Manager('zones')
  325. this.serverM = new Manager('servers')
  326. this.servertemplateM = new Manager('servertemplates', 'v2')
  327. this.serverskusM = new Manager('serverskus')
  328. this.schedulerM = new Manager('schedulers', 'v1')
  329. this.$bus.$on('VMGetPrice', (price) => {
  330. this.price = price
  331. })
  332. this.$store.dispatch('app/fetchWorkflowEnabledKeys')
  333. },
  334. watch: {
  335. 'form.fi.imageMsg': {
  336. deep: true,
  337. handler (val, oldVal) {
  338. if (R.equals(val, oldVal)) return
  339. this.$nextTick(() => {
  340. this._resetDataDisk() // 重置数据盘数据
  341. })
  342. },
  343. },
  344. isWindows (val) {
  345. const hostName = this.form.fd.hostName
  346. if (hostName) {
  347. this.hostNameValidate = {
  348. ...this.validateHostNameChange(hostName),
  349. }
  350. }
  351. },
  352. isKvm () {
  353. return this.form.fd.hypervisor === 'kvm'
  354. },
  355. },
  356. methods: {
  357. baywatch (props, watcher) {
  358. const iterator = function (prop) {
  359. this.$watch(prop, watcher)
  360. }
  361. props.forEach(iterator, this)
  362. },
  363. updateFi (fiItems) { // 子组件更新fi
  364. if (R.is(Object, fiItems)) {
  365. R.forEachObjIndexed((item, key) => {
  366. this.$set(this.form.fi, key, item)
  367. }, fiItems)
  368. }
  369. },
  370. submit (e) {
  371. e.preventDefault()
  372. this.validateForm()
  373. .then(async formData => {
  374. this.submiting = true
  375. const genCreteData = new GenCreateData(formData, this.form.fi)
  376. const data = genCreteData.all()
  377. await this.checkCreateData(data)
  378. await this.doForecast(genCreteData, data)
  379. await this.createServer(data)
  380. })
  381. .catch(error => {
  382. throw error
  383. })
  384. .finally(() => {
  385. this.submiting = false
  386. })
  387. },
  388. async checkCreateData (data) {
  389. return new this.$Manager('servers').create({ data: { ...data, dry_run: true } })
  390. },
  391. doForecast (genCreateData, data) {
  392. return new Promise((resolve, reject) => {
  393. this.schedulerM.rpc({ methodname: 'DoForecast', params: data })
  394. .then(res => {
  395. if (res.data.can_create) {
  396. resolve(data)
  397. } else {
  398. this.errors = genCreateData.getForecastErrors(res.data)
  399. reject(this.errors)
  400. }
  401. })
  402. .catch(err => {
  403. this.$message.error(i18n.t('compute.text_321', [err]))
  404. reject(err)
  405. })
  406. })
  407. },
  408. createServer (data) {
  409. return this.serverM.create({ data })
  410. .then(res => {
  411. if (R.is(Array, data.disks)) {
  412. const imageObj = data.disks.find(val => val.disk_type === 'sys')
  413. if (R.is(Object, imageObj)) {
  414. const image = imageObj.image_id
  415. storage.set(`${this.form.fi.createType}${SELECT_IMAGE_KEY_SUFFIX}`, `${this.form.fd.os}:${image}`)
  416. }
  417. }
  418. if (isSuccess(res)) {
  419. this.$message.success(i18n.t('compute.text_322'))
  420. }
  421. this.$router.push('/vminstance-container')
  422. })
  423. .catch(error => {
  424. throw error
  425. })
  426. },
  427. validateForm () {
  428. return new Promise((resolve, reject) => {
  429. this.form.fc.validateFieldsAndScroll({ scroll: { alignWithTop: true, offsetTop: 100 } }, (err, values) => {
  430. if (!err) {
  431. resolve(values)
  432. } else {
  433. this.collapseActive = ['1'] // 仅仅在报错的时候展开高级配置
  434. reject(err)
  435. }
  436. })
  437. })
  438. },
  439. cpuChange (cpu) {
  440. const memOpts = this.form.fi.cpuMem.cpu_mems_mb[cpu]
  441. if (!memOpts || !memOpts.length) { // 没有内存Opts,则内存为0
  442. let vcpu = cpu
  443. if (!this.form.fi.cpuMem.cpus.includes(cpu)) { // CPU的Opts不包括cpu的话
  444. if (this.form.fi.cpuMem.cpus && this.form.fi.cpuMem.cpus.length) { // 如果CPU的Opts有值
  445. vcpu = this.form.fi.cpuMem.cpus[0]
  446. } else { // 否则为0
  447. vcpu = 0
  448. }
  449. }
  450. this.form.fc.setFieldsValue({
  451. vcpu,
  452. vmem: 0,
  453. })
  454. return
  455. } else if (this.form.fc.getFieldValue('vcpu') !== cpu) { // 因之前未获取cpu设置为0,这一步设置回来
  456. this.form.fc.setFieldsValue({
  457. vcpu: cpu,
  458. })
  459. }
  460. this.form.fi.cpuMem.mems_mb = memOpts
  461. let defaultMem = 2048
  462. const currentMem = this.form.fc.getFieldValue('vmem')
  463. if (currentMem && this.form.fi.cpuMem.mems_mb.includes(currentMem)) {
  464. return
  465. }
  466. if (!this.form.fi.cpuMem.mems_mb.includes(2048)) { // 如果返回值不包括默认内存2G,选择第一项
  467. defaultMem = memOpts[0]
  468. }
  469. this.form.fc.setFieldsValue({
  470. vmem: defaultMem,
  471. })
  472. },
  473. _getProjectDomainInfo (variables) {
  474. variables.project = this.form.fd.project.key
  475. if (!variables.project) {
  476. variables.project = this.$store.getters.userInfo.projectName
  477. }
  478. variables.project_domain = _.get(this.form.fd, 'domain.label')
  479. if (!variables.project_domain) {
  480. variables.project_domain = this.$store.getters.userInfo.projectDomain
  481. }
  482. },
  483. _resetDataDisk () { // 重置数据盘
  484. const formValue = this.form.fc.getFieldsValue()
  485. if (formValue.dataDiskSizes) {
  486. const dataDiskKeys = Object.keys(formValue.dataDiskSizes)
  487. dataDiskKeys.forEach(key => this.$refs.dataDiskRef.decrease(key))
  488. }
  489. },
  490. _setNewFieldToFd (newField, formValue) { // vue-ant-form change 后赋值 fd
  491. const changeKeys = Object.keys(newField)
  492. R.forEachObjIndexed((item, key) => {
  493. this.$set(this.form.fd, key, item)
  494. }, newField)
  495. if (changeKeys.some(val => val.includes('dataDiskSizes'))) { // 动态赋值默认值的表单需要单独处理
  496. this.$set(this.form.fd, 'dataDiskSizes', formValue.dataDiskSizes)
  497. }
  498. },
  499. networkResourceMapper (list) {
  500. return list
  501. .map(val => {
  502. const remain = val.ports - val.ports_used
  503. if (remain <= 0) {
  504. return {
  505. ...val,
  506. __disabled: true,
  507. }
  508. }
  509. return val
  510. })
  511. .sort((a, b) => (b.ports - b.ports_used) - (a.ports - a.ports_used))
  512. },
  513. countBlur () {
  514. const count = this.form.fc.getFieldValue(this.decorators.count[0])
  515. if (!count) {
  516. this.form.fc.setFieldsValue({
  517. [this.decorators.count[0]]: 1,
  518. })
  519. }
  520. },
  521. fetchDomainCallback () {
  522. const domain = this.$route.query.domain_id
  523. if (!R.isNil(domain) && !R.isEmpty(domain)) {
  524. this.form.fc.setFieldsValue({
  525. domain: { key: domain },
  526. })
  527. }
  528. },
  529. fetchProjectCallback () {
  530. const project = this.$route.query.tenant_id
  531. if (!R.isNil(project) && !R.isEmpty(project)) {
  532. this.form.fc.setFieldsValue({
  533. project: { key: project },
  534. })
  535. }
  536. },
  537. validateHostNameChange (v) {
  538. const error = {
  539. validateStatus: 'success',
  540. errorMsg: null,
  541. }
  542. if (!v) return error
  543. if (this.isWindows) {
  544. if (v.length < 2 || v.length > 15) {
  545. error.validateStatus = 'error'
  546. error.errorMsg = this.$t('compute.validate.windows')
  547. return error
  548. }
  549. if (!/^[a-z0-9A-Z-]+$/.test(v)) {
  550. error.validateStatus = 'error'
  551. error.errorMsg = this.$t('compute.validate.windows')
  552. return error
  553. }
  554. if (/^[0-9]+$/.test(v)) { // 不能仅仅使用数字
  555. error.validateStatus = 'error'
  556. error.errorMsg = this.$t('compute.validate.others')
  557. return error
  558. }
  559. if (/(-)\1+/.test(v)) { // 不能连续使用连字符
  560. error.validateStatus = 'error'
  561. error.errorMsg = this.$t('compute.validate.others')
  562. return error
  563. }
  564. if (/^(?=(-)).*/.test(v)) { // 不能以连字符开头
  565. error.validateStatus = 'error'
  566. error.errorMsg = this.$t('compute.validate.others')
  567. return error
  568. }
  569. if (/.*?(-)$/.test(v)) { // 不能以连字符结尾
  570. error.validateStatus = 'error'
  571. error.errorMsg = this.$t('compute.validate.others')
  572. return error
  573. }
  574. } else {
  575. if (v.length < 2 || v.length > 60) {
  576. error.validateStatus = 'error'
  577. error.errorMsg = this.$t('compute.validate.others')
  578. }
  579. if (!/^[a-z0-9A-Z.-]+$/.test(v)) {
  580. error.validateStatus = 'error'
  581. error.errorMsg = this.$t('compute.validate.others')
  582. return error
  583. }
  584. if (/(\.|-)\1+/.test(v)) { // 不能连续使用点号或连字符
  585. error.validateStatus = 'error'
  586. error.errorMsg = this.$t('compute.validate.others')
  587. return error
  588. }
  589. if (/^(?=(\.|-)).*/.test(v)) { // 不能以点号或连字符开头
  590. error.validateStatus = 'error'
  591. error.errorMsg = this.$t('compute.validate.others')
  592. return error
  593. }
  594. if (/.*?(\.|-)$/.test(v)) { // 不能以点号或连字符结尾
  595. error.validateStatus = 'error'
  596. error.errorMsg = this.$t('compute.validate.others')
  597. return error
  598. }
  599. }
  600. return error
  601. },
  602. handleHostNameChange (v) {
  603. this.hostNameValidate = {
  604. ...this.validateHostNameChange(v),
  605. }
  606. },
  607. getBationServerData () {
  608. const {
  609. bastion_host_id,
  610. nodes,
  611. port,
  612. privileged_accounts,
  613. accounts,
  614. } = this.form.fd
  615. return {
  616. bastion_host_id,
  617. nodes,
  618. port,
  619. accounts: [privileged_accounts].concat(accounts),
  620. }
  621. },
  622. addShopCart () {
  623. this.validateForm()
  624. .then(async formData => {
  625. this.submiting = true
  626. const genCreateData = new GenCreateData(formData, this.form.fi)
  627. const data = genCreateData.all()
  628. // if (this.form.fd.bastion_host_enable) {
  629. // const bastionServer = this.getBationServerData()
  630. // data.bastion_server = bastionServer
  631. // }
  632. const { __count__, ...parameter } = deleteInvalid(data)
  633. const shopCart = {
  634. action: 'create',
  635. auto_execute: true,
  636. count: __count__,
  637. resource: 'servers',
  638. user_id: this.$store.getters.userInfo.id,
  639. parameter: {
  640. ...parameter,
  641. price: this.price,
  642. },
  643. }
  644. this._getProjectDomainInfo(shopCart)
  645. this.$message.success(this.$t('common.success'))
  646. this.$store.commit('shopcart/ADD_SHOP_CART', shopCart)
  647. })
  648. },
  649. handleCancel () {
  650. this.$router.push({
  651. name: 'VMContainerInstance',
  652. })
  653. },
  654. },
  655. }