index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. <template>
  2. <div>
  3. <page-header :title="$t('compute.text_892')" />
  4. <page-body needMarginBottom>
  5. <a-form :form="form.fc" class="mt-3" v-bind="formItemLayout" hideRequiredMark>
  6. <a-form-item class="mb-0" :label="$t('compute.text_297', [$t('dictionary.project')])">
  7. <domain-project :decorators="decorators.projectDomain" :fc="form.fc" :labelInValue="false" />
  8. </a-form-item>
  9. <a-form-item :label="$t('compute.text_228')">
  10. <a-input :placeholder="$t('validator.serverCreateName')" v-decorator="decorators.generate_name" />
  11. <!-- <name-repeated
  12. v-slot:extra
  13. res="scalinggroups"
  14. :default-text="$t('compute.text_893')" /> -->
  15. </a-form-item>
  16. <a-form-item :label="$t('common.description')">
  17. <a-textarea :auto-size="{ minRows: 1, maxRows: 3 }" v-decorator="decorators.description" :placeholder="$t('common_367')" />
  18. </a-form-item>
  19. <a-form-item :label="$t('compute.text_176')">
  20. <a-radio-group v-decorator="decorators.brand">
  21. <a-radio-button v-for="item in brands" :key="item.brand" :value="item.brand">{{item.label}}</a-radio-button>
  22. </a-radio-group>
  23. </a-form-item>
  24. <a-form-item :label="$t('compute.text_873')">
  25. <a-select :filterOption="filterOption" showSearch @change="handleServerTemplateChange" v-decorator="decorators.guest_template_id" :loading="serverTemplateListLoading">
  26. <a-select-option v-for="item in serverTemplateList" :row="item" :key="item.id" :value="item.id">{{item.name}}</a-select-option>
  27. </a-select>
  28. <div slot="extra">{{$t('compute.text_894')}}</div>
  29. </a-form-item>
  30. <network-selects
  31. isRequired
  32. :decorators="networlDecorators"
  33. :isDefaultFetch="false"
  34. :defaultActiveFirstOption="false"
  35. :disabled="networlDisabled"
  36. ref="NETWORK"
  37. :label="$t('compute.text_104')"
  38. :form="form"
  39. v-bind="formItemLayout"
  40. :networkParams="networkParams"
  41. :vpcParams="vpcParams" />
  42. <a-form-item :label="$t('compute.text_877')">
  43. <a-tooltip placement="top" :title="$t('compute.text_895')">
  44. <a-input-number v-decorator="decorators.max_instance_number" :min="1" :max="1000" />
  45. </a-tooltip>
  46. </a-form-item>
  47. <a-form-item :label="$t('compute.text_875')">
  48. <a-tooltip placement="top" :title="$t('compute.text_896')">
  49. <a-input-number v-decorator="decorators.desire_instance_number" :min="form.fd.min_instance_number" :max="Math.max(0, form.fd.max_instance_number)" />
  50. </a-tooltip>
  51. <div slot="extra">{{$t('compute.text_897')}}</div>
  52. </a-form-item>
  53. <a-form-item :label="$t('compute.text_876')">
  54. <a-tooltip placement="top" :title="$t('compute.text_891')">
  55. <a-input-number @blur="handleMinBlur" v-model="min" :min="0" :max="Math.max(0, form.fd.desire_instance_number)" />
  56. <a-input v-show="false" v-decorator="decorators.min_instance_number" />
  57. </a-tooltip>
  58. </a-form-item>
  59. <a-form-item :label="$t('compute.text_898')">
  60. <a-select v-decorator="decorators.shrink_principle">
  61. <a-select-option v-for="(v, k) in $t('flexGrouPprinciple')" :key="k" :value="k">{{v}}</a-select-option>
  62. </a-select>
  63. </a-form-item>
  64. <a-form-item required :label="$t('compute.text_899')">
  65. <a-radio-group v-model="isLoadbalancer">
  66. <a-radio-button :value="false">{{$t('compute.text_900')}}</a-radio-button>
  67. <a-tooltip v-if="form.fd.brand === 'Azure'" placement="top" :title="$t('compute.text_901')">
  68. <a-radio-button :disabled="true" :value="true">{{$t('compute.text_902')}}</a-radio-button>
  69. </a-tooltip>
  70. <a-radio-button v-else :value="true">{{$t('compute.text_902')}}</a-radio-button>
  71. </a-radio-group>
  72. <div v-if="isLoadbalancer" style="max-width: 920px">
  73. <bind-lb :fc="form.fc" ref="BIND_LB" />
  74. </div>
  75. </a-form-item>
  76. <a-form-item :label="$t('compute.text_903')">
  77. <a-select v-decorator="decorators.health_check_mode">
  78. <template v-for="(v, k) in $t('flexGroupHealthCheckMode')">
  79. <a-select-option v-if="k !== 'loadbalancer' || (isLoadbalancer && k === 'loadbalancer')" :key="k" :value="k">{{v}}</a-select-option>
  80. </template>
  81. </a-select>
  82. </a-form-item>
  83. <a-form-item :label="$t('compute.text_904')">
  84. <a-select v-decorator="decorators.health_check_cycle">
  85. <a-select-option v-for="(v, k) in $t('flexGroupCycles')" :key="k" :value="parseInt(k)">{{v}}</a-select-option>
  86. </a-select>
  87. </a-form-item>
  88. <a-form-item :label="$t('compute.text_905')">
  89. <a-input-number :min="1" v-decorator="decorators.health_check_gov" />{{$t('compute.text_767')}}<div slot="extra">{{$t('compute.text_906')}}</div>
  90. </a-form-item>
  91. </a-form>
  92. </page-body>
  93. <page-footer>
  94. <div slot="right">
  95. <a-button class="mr-3" type="primary" @click="handleConfirm" :loading="loading">{{$t('compute.text_907')}}</a-button>
  96. <a-button @click="handleCancel">{{$t('compute.text_908')}}</a-button>
  97. </div>
  98. </page-footer>
  99. </div>
  100. </template>
  101. <script>
  102. import { HYPERVISORS_MAP } from '@/constants'
  103. import DomainProject from '@/sections/DomainProject'
  104. // import NameRepeated from '@/sections/NameRepeated'
  105. import NetworkSelects from '@/sections/NetworkSelects'
  106. import { getInitialValue } from '@/utils/common/ant'
  107. import { typeClouds } from '@/utils/common/hypervisor'
  108. import BindLb from '../components/BindLb'
  109. import { DECORATORS, BRANDS } from '../constants'
  110. export default {
  111. name: 'ScalingGroupCreae',
  112. components: {
  113. BindLb,
  114. DomainProject,
  115. // NameRepeated,
  116. NetworkSelects,
  117. },
  118. data () {
  119. const initFd = getInitialValue(DECORATORS)
  120. return {
  121. min: 0,
  122. BRANDS,
  123. decorators: DECORATORS,
  124. loading: false,
  125. isLoadbalancer: false,
  126. serverTemplateListLoading: false,
  127. serverTemplateList: [],
  128. serverTemplate: {},
  129. healthCheckModeList: [],
  130. networlDisabled: false,
  131. form: {
  132. fc: this.$form.createForm(this, {
  133. onValuesChange: this.handleValuesChange,
  134. }),
  135. fd: { ...initFd },
  136. },
  137. formItemLayout: {
  138. wrapperCol: {
  139. md: { span: 16 },
  140. xl: { span: 18 },
  141. xxl: { span: 20 },
  142. },
  143. labelCol: {
  144. md: { span: 8 },
  145. xl: { span: 6 },
  146. xxl: { span: 4 },
  147. },
  148. },
  149. isDeleteVpc: false,
  150. isDeleteNetwork: false,
  151. }
  152. },
  153. provide () {
  154. return {
  155. form: this.form,
  156. }
  157. },
  158. computed: {
  159. capabilityBrands () {
  160. return this.$store.getters.capability.brands
  161. },
  162. brands () {
  163. let supportBrands = BRANDS.filter(brand => {
  164. return this.capabilityBrands.indexOf(brand) > -1
  165. })
  166. supportBrands = [...Object.values(HYPERVISORS_MAP)].filter(item => {
  167. return supportBrands.includes(item.brand) && item.hypervisor !== HYPERVISORS_MAP.pod.key
  168. })
  169. return supportBrands
  170. },
  171. project_domain () {
  172. return this.form.fd.domain ? this.form.fd.domain : this.$store.getters.userInfo.projectDomainId
  173. },
  174. project () {
  175. return this.form.fd.project ? this.form.fd.project : this.$store.getters.userInfo.projectId
  176. },
  177. scopeParams () {
  178. if (this.$store.getters.isAdminMode) {
  179. return {
  180. project_domain: this.project_domain,
  181. tenant: this.form.fd.project,
  182. }
  183. }
  184. return { scope: this.$store.getters.scope }
  185. },
  186. networlDecorators () {
  187. return {
  188. vpc: ['vpc', {
  189. validateFirst: true,
  190. rules: [
  191. { required: true, message: this.$t('compute.text_194') },
  192. {
  193. validator: (rule, value, _cb) => {
  194. if (this.isDeleteVpc) {
  195. return _cb(this.$t('compute.text_909'))
  196. }
  197. _cb()
  198. },
  199. },
  200. ],
  201. }],
  202. network: ['network', {
  203. validateFirst: true,
  204. rules: [
  205. { required: true, message: this.$t('compute.text_195') },
  206. {
  207. validator: (rule, value, _cb) => {
  208. if (this.isDeleteNetwork) {
  209. return _cb(this.$t('compute.text_909'))
  210. }
  211. _cb()
  212. },
  213. },
  214. ],
  215. }],
  216. }
  217. },
  218. },
  219. watch: {
  220. isLoadbalancer (v) {
  221. if (v) {
  222. this.$nextTick(() => {
  223. this.$refs.BIND_LB.fetchQueryLbs(this.form.fd.vpc)
  224. })
  225. }
  226. this.form.fc.setFieldsValue({
  227. health_check_mode: v ? 'loadbalancer' : 'normal',
  228. })
  229. },
  230. },
  231. created () {
  232. this.form.fc.getFieldDecorator('cloudregion', { preserve: true })
  233. },
  234. methods: {
  235. filterOption (input, option) {
  236. return (
  237. option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
  238. )
  239. },
  240. async handleMinBlur ({ target }) {
  241. const val = parseInt(target.value || 0)
  242. const { setFieldsValue } = this.form.fc
  243. await this.$nextTick()
  244. setFieldsValue({
  245. min_instance_number: val || undefined,
  246. })
  247. },
  248. // domainChange () {
  249. // this.fetchQueryTs()
  250. // this.$refs['NETWORK'].fetchs()
  251. // },
  252. projectChange () {
  253. if (this.brands && this.brands.length > 0) {
  254. this.form.fc.setFieldsValue({
  255. brand: this.brands[0].brand,
  256. })
  257. }
  258. },
  259. vpcChange () {
  260. if (this.isLoadbalancer) {
  261. this.$refs.BIND_LB.fetchQueryLbs(this.form.fd.vpc)
  262. }
  263. },
  264. brandChange () {
  265. const { brand } = this.form.fd
  266. if (brand === 'Azure') {
  267. this.isLoadbalancer = false
  268. }
  269. this.fetchQueryTs()
  270. },
  271. templateChange () {
  272. this.isDeleteVpc = false
  273. this.isDeleteNetwork = false
  274. const { fc } = this.form
  275. this.$refs.NETWORK.fetchs(async (rets) => {
  276. await this.$nextTick()
  277. const { vpcList, networkList } = rets
  278. // 判断当前选择的主机模板中的VPC是否存在VPC列表中
  279. const _vpcId = fc.getFieldValue('vpc')
  280. if (vpcList !== undefined && _vpcId) {
  281. this.isDeleteVpc = vpcList.length === 0 || !vpcList.find(item => item.id === _vpcId)
  282. fc.validateFields(['vpc'])
  283. }
  284. // 判断当前选择的主机模板中的netwrok是否存在netwrok列表中
  285. const _networkId = fc.getFieldValue('network')
  286. if (networkList !== undefined && _networkId) {
  287. this.isDeleteNetwork = networkList.length === 0 || !networkList.find(item => item.id === _networkId)
  288. fc.validateFields(['network'])
  289. }
  290. })
  291. },
  292. async handleValuesChange (vm, changedFields) {
  293. this.form.fd = {
  294. ...this.form.fd,
  295. ...changedFields,
  296. }
  297. await this.$nextTick()
  298. if (changedFields.project) {
  299. this.projectChange()
  300. }
  301. if (changedFields.brand) {
  302. this.brandChange()
  303. }
  304. if (changedFields.guest_template_id) {
  305. this.templateChange()
  306. }
  307. if (changedFields.vpc || Object.keys(changedFields).indexOf('vpc') > -1) {
  308. this.vpcChange()
  309. }
  310. },
  311. vpcParams () {
  312. const { brand } = this.form.fd
  313. return {
  314. brand,
  315. ...this.scopeParams,
  316. region: this.serverTemplate.region_id,
  317. }
  318. },
  319. networkParams () {
  320. return {
  321. ...this.scopeParams,
  322. zone: this.serverTemplate.zone_id,
  323. }
  324. },
  325. setNetworkValues (row) {
  326. if (row.config_info && row.config_info.nets && row.config_info.nets.length > 0) {
  327. const net = row.config_info.nets[0]
  328. if (net.vpc_id && net.id) {
  329. this.form.fc.setFieldsValue({
  330. vpc: net.vpc_id,
  331. network: net.id,
  332. })
  333. this.networlDisabled = true
  334. return true
  335. }
  336. }
  337. this.form.fc.setFieldsValue({
  338. vpc: undefined,
  339. network: undefined,
  340. })
  341. this.networlDisabled = false
  342. },
  343. handleServerTemplateChange (e, { data }) {
  344. const { row } = data.attrs
  345. this.serverTemplate = row
  346. this.setNetworkValues(row)
  347. this.form.fc.setFieldsValue({
  348. cloudregion: row.cloudregion_id,
  349. })
  350. },
  351. async fetchQueryTs () {
  352. const manager = new this.$Manager('servertemplates')
  353. // console.log(this.form.fd)
  354. const { brand } = this.form.fd
  355. this.serverTemplateListLoading = true
  356. try {
  357. const { data } = await manager.list({
  358. params: {
  359. limit: 0,
  360. brand,
  361. filter: 'status.in(ready)',
  362. ...this.scopeParams,
  363. },
  364. })
  365. if (data.data && data.data.length > 0) {
  366. const list = data.data
  367. this.serverTemplateList = list.filter(t => {
  368. const isNats = t.config_info && t.config_info.nets && t.config_info.nets.length === 1
  369. return isNats
  370. })
  371. this.serverTemplate = this.serverTemplateList[0]
  372. this.setNetworkValues(this.serverTemplateList[0])
  373. this.form.fc.setFieldsValue({
  374. cloudregion: this.serverTemplateList[0].cloudregion_id,
  375. })
  376. } else {
  377. this.serverTemplateList = []
  378. this.networlDisabled = false
  379. this.form.fc.setFieldsValue({
  380. vpc: undefined,
  381. network: undefined,
  382. })
  383. this.$refs.NETWORK.networkList = []
  384. this.$refs.NETWORK.vpcList = []
  385. }
  386. } catch (err) {
  387. throw err
  388. } finally {
  389. this.serverTemplateListLoading = false
  390. const list = this.serverTemplateList
  391. this.form.fc.setFieldsValue({
  392. guest_template_id: (list && list.length > 0) ? list[0].id : undefined,
  393. })
  394. }
  395. },
  396. formatValues (values) {
  397. const { brand, network } = values
  398. if (network) {
  399. values.networks = [network]
  400. delete values.network
  401. }
  402. if (brand && typeClouds.brandMap[brand]) {
  403. values.hypervisor = typeClouds.brandMap[brand].hypervisor
  404. }
  405. return values
  406. },
  407. handleCancel () {
  408. this.$router.push({
  409. name: 'ScalingGroup',
  410. })
  411. },
  412. async handleConfirm () {
  413. const { validateFields } = this.form.fc
  414. const manager = new this.$Manager('scalinggroups', 'v1')
  415. try {
  416. const values = await validateFields()
  417. this.loading = true
  418. await manager.create({
  419. data: Object.assign({}, this.formatValues(values)),
  420. })
  421. this.handleCancel()
  422. } catch (err) {
  423. throw err
  424. } finally {
  425. this.loading = false
  426. }
  427. },
  428. },
  429. }
  430. </script>
  431. <style>
  432. </style>