EditRules.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <template>
  2. <base-dialog @cancel="cancelDialog">
  3. <div slot="header">{{params.title === 'edit' ? $t('compute.text_982') : params.title === 'create' ? $t('compute.perform_create') : $t('compute.text_983')}}{{$t('compute.rule')}}</div>
  4. <div slot="body">
  5. <a-form
  6. v-bind="formItemLayout"
  7. :form="form.fc">
  8. <a-form-item :label="$t('compute.text_175')" v-if="params.title !== 'edit'">
  9. <a-select
  10. v-decorator="decorators.type"
  11. :disabled="typeDisabled"
  12. :placeholder="$t('compute.text_219')"
  13. @change="typeChange">
  14. <a-select-option v-for="item in typeOptions" :key="item.value" :value="item.value">
  15. {{item.label}}
  16. </a-select-option>
  17. </a-select>
  18. </a-form-item>
  19. <a-form-item :label="decLabel" :extra="$t('compute.text_995')">
  20. <a-select
  21. mode="combobox"
  22. @change="fetchSecgroups"
  23. option-filter-prop="children"
  24. :placeholder="$t('compute.secgroup.source.placeholder')"
  25. :disabled="cidrDisabled || cidrChecked"
  26. v-decorator="decorators.source">
  27. <a-select-opt-group>
  28. <span slot="label">CIDR</span>
  29. <a-select-option v-for="item in cidrOptions" :key="item.value" :value="item.value">
  30. {{item.label}}
  31. </a-select-option>
  32. </a-select-opt-group>
  33. <!-- <a-select-opt-group>
  34. <span slot="label">{{$t('dictionary.secgroup')}}</span>
  35. <a-select-option v-for="item in secgroupOpts" :key="item.id" :value="item.name">
  36. {{item.name}}
  37. </a-select-option>
  38. </a-select-opt-group> -->
  39. </a-select>
  40. <a-checkbox v-if="cidrCheckedShow" class="right-checkbox" @change="cidrChange" :checked="cidrChecked">{{$t('compute.any_cidr.text')}}</a-checkbox>
  41. </a-form-item>
  42. <a-form-item :label="$t('compute.text_980')">
  43. <a-select v-decorator="decorators.protocol" @change="protocolChange" :disabled="protocolDisabled || cidrDisabled">
  44. <a-select-option v-for="item in protocolOptions" :key="item.value" :value="item.value">
  45. {{item.label}}
  46. </a-select-option>
  47. </a-select>
  48. </a-form-item>
  49. <a-form-item :label="$t('compute.text_998')" :extra="portExtra">
  50. <a-input :disabled="portsDisabled || cidrDisabled" v-decorator="decorators.ports" :placeholder="$t('compute.text_350')" />
  51. <a-checkbox class="right-checkbox" @change="portsChange" :checked="portsChecked" :disabled="portsCheckboxDisabled || cidrDisabled">{{$t('compute.text_1000')}}</a-checkbox>
  52. </a-form-item>
  53. <a-form-item :label="$t('compute.text_694')">
  54. <a-select v-decorator="decorators.action" :disabled="!isPrioritySupport || cidrDisabled">
  55. <a-select-option v-for="item in actionOptions" :key="item.value" :value="item.value">
  56. {{item.label}}
  57. </a-select-option>
  58. </a-select>
  59. </a-form-item>
  60. <a-form-item v-if="isPrioritySupport" :label="$t('compute.text_1001')" :extra="priorityExtra">
  61. <a-input-number :min="priorityMin" :max="priorityMax" :disabled="priorityDisabled || cidrDisabled" v-decorator="decorators.priority" />
  62. </a-form-item>
  63. <a-form-item :extra="isAws ? $t('compute.use_en_comment') : ''">
  64. <span slot="label">{{$t('compute.text_312')}}</span>
  65. <a-input v-decorator="decorators.description" :placeholder="$t('common_367')" />
  66. </a-form-item>
  67. </a-form>
  68. </div>
  69. <div slot="footer">
  70. <a-button type="primary" @click="handleConfirm" :loading="loading">{{ $t('dialog.ok') }}</a-button>
  71. <a-button @click="cancelDialog">{{ $t('dialog.cancel') }}</a-button>
  72. </div>
  73. </base-dialog>
  74. </template>
  75. <script>
  76. import { mapGetters } from 'vuex'
  77. import { REGEXP } from '@/utils/validate'
  78. import DialogMixin from '@/mixins/dialog'
  79. import WindowsMixin from '@/mixins/windows'
  80. import { priorityRuleMap } from '../constants'
  81. export default {
  82. name: 'EditRulesDialog',
  83. mixins: [DialogMixin, WindowsMixin],
  84. data () {
  85. const selectItem = this.params.data[0]
  86. const { brand, cloud_env } = this.params
  87. const item = brand ? priorityRuleMap[brand.toLowerCase()] || {} : {}
  88. const priorityMin = !item.priorityNoSupport ? item.min : 1
  89. const priorityMax = !item.priorityNoSupport ? item.max : 1
  90. let cidrChecked = JSON.stringify(selectItem) === '{}' ? true : !selectItem.cidr
  91. let cidrCheckedShow = true
  92. if (cloud_env === 'public') {
  93. cidrCheckedShow = false
  94. cidrChecked = false
  95. }
  96. return {
  97. loading: false,
  98. form: {
  99. fc: this.$form.createForm(this),
  100. },
  101. decorators: {
  102. type: [
  103. 'type',
  104. ],
  105. source: [
  106. 'source',
  107. {
  108. validateFirst: true,
  109. initialValue: selectItem.cidr || (cidrCheckedShow ? 'ALL' : '0.0.0.0/0'),
  110. rules: [
  111. { validator: this.validateCIDR, required: !cidrCheckedShow },
  112. ],
  113. },
  114. ],
  115. protocol: [
  116. 'protocol',
  117. {
  118. initialValue: selectItem.protocol || 'tcp',
  119. },
  120. ],
  121. ports: [
  122. 'ports',
  123. {
  124. validateFirst: true,
  125. initialValue: JSON.stringify(selectItem) === '{}' ? '' : selectItem.ports ? selectItem.ports : 'ALL',
  126. rules: [
  127. { required: true, message: this.$t('compute.text_347') },
  128. { validator: this.validatePorts },
  129. ],
  130. },
  131. ],
  132. action: [
  133. 'action',
  134. {
  135. initialValue: selectItem.action || 'allow',
  136. rules: [
  137. { required: true },
  138. ],
  139. },
  140. ],
  141. priority: [
  142. 'priority',
  143. {
  144. initialValue: selectItem.priority || priorityMin,
  145. },
  146. ],
  147. description: [
  148. 'description',
  149. {
  150. initialValue: selectItem.description || '',
  151. rules: [
  152. {
  153. validator: (rule, value, callback) => {
  154. if (value && (this.isKsyun || this.isAws)) {
  155. if (/[\u4e00-\u9fff]/.test(value)) {
  156. callback(new Error(this.$t('scope.text_339')))
  157. }
  158. }
  159. callback()
  160. },
  161. },
  162. ],
  163. },
  164. ],
  165. },
  166. formItemLayout: {
  167. wrapperCol: {
  168. span: 15,
  169. },
  170. labelCol: {
  171. span: 3,
  172. },
  173. },
  174. typeOptions: [
  175. { label: this.$t('compute.text_144'), value: 'custom', description: '' },
  176. { label: this.$t('compute.text_1003'), value: 'windows', description: this.$t('compute.text_1004') },
  177. { label: 'SSH (22)', value: 'linux', description: this.$t('compute.text_1005') },
  178. { label: 'HTTP(80)', value: 'http', description: this.$t('compute.text_1006') },
  179. { label: 'HTTPS(443)', value: 'https', description: this.$t('compute.text_1007') },
  180. { label: 'Ping', value: 'ping', description: this.$t('compute.text_1008') },
  181. ],
  182. actionOptions: [
  183. { label: this.$t('compute.text_976'), value: 'allow' },
  184. { label: this.$t('compute.text_977'), value: 'deny' },
  185. ],
  186. cidrOptions: [
  187. // { label: this.$t('compute.any_cidr.text'), value: '' },
  188. { label: '0.0.0.0/0', value: '0.0.0.0/0' },
  189. { label: '10.0.0.0/8', value: '10.0.0.0/8' },
  190. { label: '172.16.0.0/12', value: '172.16.0.0/12' },
  191. { label: '192.168.0.0/16', value: '192.168.0.0/16' },
  192. { label: '::/0', value: '::/0' },
  193. { label: 'fd::/64', value: 'fd::/64' },
  194. ],
  195. secgroupOpts: [
  196. ],
  197. cidrChecked,
  198. cidrCheckedShow,
  199. typeDisabled: false,
  200. portsDisabled: JSON.stringify(selectItem) === '{}' ? false : !selectItem.ports,
  201. portsCheckboxDisabled: selectItem.protocol === 'any' || selectItem.protocol === 'icmp',
  202. portsChecked: JSON.stringify(selectItem) === '{}' ? false : !selectItem.ports,
  203. decLabel: this.params.type === 'in' ? this.$t('compute.text_979') : this.$t('compute.text_978'),
  204. protocolDisabled: this.params.title !== 'edit',
  205. priorityMin,
  206. priorityMax,
  207. isPrioritySupport: !item.priorityNoSupport,
  208. priorityItem: item,
  209. cloud_env,
  210. }
  211. },
  212. computed: {
  213. ...mapGetters(['scope']),
  214. priorityExtra () {
  215. if (this.priorityItem.priorityNoSupport) {
  216. return ''
  217. } else if (this.priorityItem.max) {
  218. return `${this.priorityItem.min}-${this.priorityItem.max}, ${this.priorityItem.isMaxHigh ? this.$t('compute.secgroup_priority_tip2') : this.$t('compute.secgroup_priority_tip')}${this.priorityItem.noRepeat ? ', ' + this.$t('compute.secgroup_priority_no_repeat') : ''}`
  219. }
  220. return this.$t('compute.text_1002')
  221. },
  222. portExtra () {
  223. if (this.priorityItem.portSupportComma) {
  224. return this.$t('compute.text_999')
  225. }
  226. return this.$t('compute.text_999_1')
  227. },
  228. isAws () {
  229. return this.params.brand === 'Aws'
  230. },
  231. isKsyun () {
  232. return this.params.brand === 'Ksyun'
  233. },
  234. protocolOptions () {
  235. let ret = [
  236. { label: 'TCP', value: 'tcp' },
  237. { label: 'UDP', value: 'udp' },
  238. { label: 'ICMP', value: 'icmp' },
  239. { label: this.$t('compute.any_protocol.text'), value: 'any' },
  240. ]
  241. if (this.params.brand && this.params.brand.toLowerCase() === 'ucloud') {
  242. ret = ret.filter(item => item.value !== 'any')
  243. }
  244. return ret
  245. },
  246. cidrDisabled () {
  247. if (this.params.title === 'edit' && ['ctyun'].includes(this.params.brand.toLowerCase())) {
  248. return true
  249. }
  250. return false
  251. },
  252. priorityDisabled () {
  253. return this.params.title === 'edit' && ['qcloud'].includes(this.params.brand.toLowerCase())
  254. },
  255. },
  256. created () {
  257. // this.fetchSecgroups('')
  258. },
  259. methods: {
  260. validatePorts (rule, value, callback) {
  261. const portReg = /^\d+$/
  262. if (value === 'ALL') {
  263. callback()
  264. return
  265. }
  266. if (!value) {
  267. callback(new Error(this.$t('validator.ports')))
  268. return
  269. }
  270. if (!this.priorityItem.portSupportComma && value.indexOf(',') !== -1) {
  271. callback(new Error(this.$t('validator.ports')))
  272. return
  273. }
  274. if (value.indexOf(',') !== -1) {
  275. const ports = value.split(',')
  276. if (ports.some(item => !portReg.test(item) || +item < 0 || +item > 65535)) {
  277. callback(new Error(this.$t('validator.ports')))
  278. return
  279. }
  280. } else if (value.indexOf('-') !== -1) {
  281. const ports = value.split('-')
  282. if (ports.length !== 2 || ports.some(item => !portReg.test(item) || +item < 3306 || +item > 20000)) {
  283. callback(new Error(this.$t('validator.ports')))
  284. return
  285. }
  286. } else if (!portReg.test(value) || +value < 0 || +value > 65535) {
  287. callback(new Error(this.$t('validator.ports')))
  288. return
  289. }
  290. callback()
  291. },
  292. validateCIDR (rule, value, callback) {
  293. if (!this.cidrCheckedShow && !value) {
  294. callback(new Error(this.$t('common.tips.input', [this.decLabel])))
  295. }
  296. if (this.cidrChecked || !value) {
  297. callback()
  298. } else if (!REGEXP.IPv6.regexp.test(value) && !REGEXP.IPv4.regexp.test(value) && !REGEXP.cidr.regexp.test(value) && !REGEXP.cidr6.regexp.test(value)) {
  299. callback(new Error(this.$t('common.tips.input', ['IPv6'])))
  300. }
  301. callback()
  302. },
  303. changeCidrChecked (bool) {
  304. this.cidrChecked = this.cidrCheckedShow ? bool : this.cidrCheckedShow
  305. },
  306. typeChange (e) {
  307. if (e === 'windows') {
  308. this.form.fc.setFieldsValue({ ports: '3389', protocol: 'tcp' })
  309. this.changeCidrChecked(true)
  310. this.portsChecked = false
  311. this.portsDisabled = true
  312. this.portsCheckboxDisabled = true
  313. this.protocolDisabled = true
  314. } else if (e === 'linux') {
  315. this.form.fc.setFieldsValue({ ports: '22', protocol: 'tcp' })
  316. this.changeCidrChecked(true)
  317. this.portsChecked = false
  318. this.portsDisabled = true
  319. this.portsCheckboxDisabled = true
  320. this.protocolDisabled = true
  321. } else if (e === 'http') {
  322. this.form.fc.setFieldsValue({ ports: '80', protocol: 'tcp' })
  323. this.changeCidrChecked(true)
  324. this.portsChecked = false
  325. this.portsDisabled = true
  326. this.portsCheckboxDisabled = true
  327. this.protocolDisabled = true
  328. } else if (e === 'https') {
  329. this.form.fc.setFieldsValue({ ports: '443', protocol: 'tcp' })
  330. this.changeCidrChecked(true)
  331. this.portsChecked = false
  332. this.portsDisabled = true
  333. this.portsCheckboxDisabled = true
  334. this.protocolDisabled = true
  335. } else if (e === 'ping') {
  336. this.form.fc.setFieldsValue({ ports: 'ALL', protocol: 'icmp' })
  337. this.changeCidrChecked(true)
  338. this.portsChecked = true
  339. this.portsDisabled = true
  340. this.portsCheckboxDisabled = true
  341. this.protocolDisabled = true
  342. } else {
  343. this.changeCidrChecked(true)
  344. this.portsChecked = false
  345. this.portsDisabled = false
  346. this.form.fc.resetFields(['ports'])
  347. this.portsCheckboxDisabled = false
  348. this.protocolDisabled = false
  349. }
  350. },
  351. protocolChange (e) {
  352. if (e === 'icmp') {
  353. this.portsChecked = true
  354. this.portsDisabled = true
  355. this.$nextTick(() => {
  356. this.form.fc.setFieldsValue({ ports: 'ALL' })
  357. })
  358. this.portsCheckboxDisabled = true
  359. } else {
  360. if (e === 'any') {
  361. this.portsChecked = true
  362. this.$nextTick(() => {
  363. this.form.fc.setFieldsValue({ ports: 'ALL' })
  364. })
  365. this.portsCheckboxDisabled = true
  366. this.portsDisabled = true
  367. this.typeDisabled = true
  368. } else {
  369. this.portsCheckboxDisabled = false
  370. this.typeDisabled = false
  371. }
  372. }
  373. },
  374. // async fetchSecgroups (value) {
  375. // const params = {
  376. // filter: [
  377. // `id.notequals("${this.params.secgroup}")`,
  378. // ],
  379. // scope: this.scope,
  380. // limit: 10,
  381. // }
  382. // if (value.length > 0) {
  383. // params.filter.push(`name.contains("${value}")`)
  384. // }
  385. // await new this.$Manager('secgroups').list({ params })
  386. // .then(({ data: { data = [] } }) => {
  387. // this.secgroupOpts.splice(0, this.secgroupOpts.length, ...data)
  388. // })
  389. // },
  390. portsChange (e) {
  391. this.portsChecked = !this.portsChecked
  392. this.portsDisabled = !this.portsDisabled
  393. if (e.target.checked) {
  394. this.$nextTick(() => {
  395. this.form.fc.setFieldsValue({ ports: 'ALL' })
  396. })
  397. } else {
  398. this.$nextTick(() => {
  399. this.form.fc.setFieldsValue({ ports: '' })
  400. })
  401. }
  402. },
  403. cidrChange (e) {
  404. this.cidrChecked = !this.cidrChecked
  405. if (e.target.checked) {
  406. this.$nextTick(() => {
  407. this.form.fc.setFieldsValue({ source: 'ALL' })
  408. })
  409. } else {
  410. this.$nextTick(() => {
  411. this.form.fc.setFieldsValue({ source: '0.0.0.0/0' })
  412. })
  413. }
  414. },
  415. saveEdit (data) {
  416. if (this.params.title === 'clone') {
  417. let description = ''
  418. this.typeOptions.forEach((item) => {
  419. if (item.value === data.type) {
  420. description = item.description
  421. }
  422. })
  423. const params = {
  424. ...this.params.data[0],
  425. ...data,
  426. secgroup: this.params.secgroup,
  427. description: data.description && data.description.length > 0 ? data.description : description,
  428. }
  429. return new this.$Manager('secgrouprules').create({
  430. data: params,
  431. })
  432. } else if (this.params.title === 'create') {
  433. let description = ''
  434. this.typeOptions.forEach((item) => {
  435. if (item.value === data.type) {
  436. description = item.description
  437. }
  438. })
  439. const params = {
  440. ...data,
  441. secgroup: this.params.secgroup,
  442. description: data.description && data.description.length > 0 ? data.description : description,
  443. direction: this.params.type,
  444. }
  445. return new this.$Manager('secgrouprules').create({
  446. data: params,
  447. })
  448. } else {
  449. const id = this.params.data[0].id
  450. return new this.$Manager('secgrouprules').update({
  451. id,
  452. data,
  453. })
  454. }
  455. },
  456. async handleConfirm () {
  457. this.loading = true
  458. try {
  459. const values = await this.form.fc.validateFields()
  460. if (values.ports === 'ALL') {
  461. values.ports = ''
  462. }
  463. if (this.cidrChecked) {
  464. values.cidr = ''
  465. if (values.source) {
  466. delete values.source
  467. }
  468. } else if (values.source) {
  469. // const isCidr = validate(values.source, 'cidr')
  470. // if (!isCidr || isCidr.result === false) {
  471. // values.peer_secgroup_id = values.source
  472. // } else {
  473. values.cidr = values.source
  474. // }
  475. delete values.source
  476. } else {
  477. values.cidr = ''
  478. }
  479. await this.saveEdit(values)
  480. this.loading = false
  481. this.params.refresh && this.params.refresh()
  482. this.cancelDialog()
  483. } catch (e) {
  484. this.loading = false
  485. throw e
  486. }
  487. },
  488. },
  489. }
  490. </script>
  491. <style lang="less" scoped>
  492. .right-checkbox {
  493. width: 100px;
  494. height: 40px;
  495. left: 500px;
  496. font-size: 12px!important;
  497. color: #ccc;
  498. position: absolute;
  499. }
  500. </style>