Update.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. <template>
  2. <base-dialog :width="700" @cancel="cancelDialog">
  3. <div slot="header">{{ $t('table.action.modify') }} - {{ $t('dictionary.keypair') }}</div>
  4. <div slot="body">
  5. <a-spin :spinning="loadingDetail">
  6. <a-form v-if="!loadingDetail" :form="form.fc" hideRequiredMark v-bind="formItemLayout">
  7. <a-form-item :label="$t('common.name')">
  8. <a-input
  9. v-decorator="decorators.name"
  10. :placeholder="$t('common.tips.input', [$t('common.name')])" />
  11. </a-form-item>
  12. <a-form-item :label="$t('dictionary.keypair')" :extra="$t('aice.container_secret.env_hint') + ' ' + $t('aice.container_secret.add_pair')">
  13. <div v-for="(item, index) in items" :key="item.id" class="d-flex align-items-start mb-2">
  14. <a-input
  15. v-model="item.key"
  16. :placeholder="$t('aice.container_secret.key')"
  17. class="mr-2"
  18. style="flex: 1; min-width: 0" />
  19. <span class="mr-2 mt-2">=</span>
  20. <a-input
  21. v-model="item.value"
  22. :placeholder="$t('aice.container_secret.value')"
  23. class="mr-2"
  24. style="flex: 1; min-width: 0" />
  25. <a-button
  26. type="danger"
  27. shape="circle"
  28. icon="delete"
  29. size="small"
  30. class="mt-1"
  31. :disabled="items.length <= 1"
  32. @click="removeRow(index)" />
  33. </div>
  34. <a-button type="dashed" block icon="plus" @click="addRow">
  35. {{ $t('aice.container_secret.add_pair') }}
  36. </a-button>
  37. <div v-if="blobError" class="text-danger mt-1">{{ blobError }}</div>
  38. </a-form-item>
  39. </a-form>
  40. </a-spin>
  41. </div>
  42. <div slot="footer">
  43. <a-button type="primary" :loading="loading" :disabled="loadingDetail" @click="handleConfirm">{{ $t('dialog.ok') }}</a-button>
  44. <a-button :disabled="loadingDetail" @click="cancelDialog">{{ $t('dialog.cancel') }}</a-button>
  45. </div>
  46. </base-dialog>
  47. </template>
  48. <script>
  49. import { uuid } from '@/utils/utils'
  50. import DialogMixin from '@/mixins/dialog'
  51. import WindowsMixin from '@/mixins/windows'
  52. function parseBlob (blob) {
  53. if (blob == null) return {}
  54. if (typeof blob === 'object') return blob
  55. if (typeof blob === 'string') {
  56. try {
  57. return JSON.parse(blob) || {}
  58. } catch (e) {
  59. return {}
  60. }
  61. }
  62. return {}
  63. }
  64. export default {
  65. name: 'ContainerSecretUpdateDialog',
  66. mixins: [DialogMixin, WindowsMixin],
  67. data () {
  68. return {
  69. loading: false,
  70. loadingDetail: true,
  71. blobError: '',
  72. initialName: '',
  73. items: [{ id: uuid(), key: '', value: '' }],
  74. form: {
  75. fc: this.$form.createForm(this, {
  76. onValuesChange: (props, values) => {
  77. Object.keys(values).forEach((key) => {
  78. this.$set(this.form.fd, key, values[key])
  79. })
  80. },
  81. }),
  82. fd: {},
  83. },
  84. formItemLayout: {
  85. wrapperCol: { span: 20 },
  86. labelCol: { span: 4 },
  87. },
  88. }
  89. },
  90. computed: {
  91. recordId () {
  92. const data = this.params.data
  93. const row = Array.isArray(data) ? data[0] : data
  94. return row?.id
  95. },
  96. decorators () {
  97. return {
  98. name: [
  99. 'name',
  100. {
  101. initialValue: this.initialName,
  102. rules: [
  103. { required: true, message: this.$t('common.tips.input', [this.$t('common.name')]) },
  104. ],
  105. },
  106. ],
  107. }
  108. },
  109. },
  110. created () {
  111. this.fetchDetail()
  112. },
  113. methods: {
  114. async fetchDetail () {
  115. if (!this.recordId) {
  116. this.loadingDetail = false
  117. return
  118. }
  119. this.loadingDetail = true
  120. try {
  121. const manager = new this.$Manager('credentials', 'v1')
  122. const { data } = await manager.get({ id: this.recordId })
  123. const row = data || (Array.isArray(this.params.data) ? this.params.data[0] : this.params.data) || {}
  124. this.initialName = row.name || ''
  125. const blob = parseBlob(row.blob)
  126. const entries = Object.entries(blob)
  127. this.items = entries.length
  128. ? entries.map(([k, v]) => ({ id: uuid(), key: k, value: String(v) }))
  129. : [{ id: uuid(), key: '', value: '' }]
  130. } catch (e) {
  131. this.$message.error(this.$t('common.get_failed'))
  132. this.cancelDialog()
  133. } finally {
  134. this.loadingDetail = false
  135. }
  136. },
  137. addRow () {
  138. this.items.push({ id: uuid(), key: '', value: '' })
  139. this.blobError = ''
  140. },
  141. removeRow (index) {
  142. if (this.items.length <= 1) return
  143. this.items.splice(index, 1)
  144. this.blobError = ''
  145. },
  146. buildBlob () {
  147. const blob = {}
  148. for (const item of this.items) {
  149. const k = (item.key || '').trim()
  150. const v = (item.value || '').trim()
  151. if (k) blob[k] = v
  152. }
  153. return blob
  154. },
  155. async handleConfirm () {
  156. this.blobError = ''
  157. const values = await this.form.fc.validateFields().catch(() => null)
  158. if (!values) return
  159. const blob = this.buildBlob()
  160. if (Object.keys(blob).length === 0) {
  161. this.blobError = this.$t('common.tips.input', [this.$t('aice.container_secret')])
  162. return
  163. }
  164. this.loading = true
  165. try {
  166. const manager = new this.$Manager('credentials', 'v1')
  167. await manager.update({
  168. id: this.recordId,
  169. data: {
  170. name: values.name || '',
  171. blob,
  172. },
  173. })
  174. this.$message.success(this.$t('common.success'))
  175. this.params.callback && this.params.callback()
  176. this.cancelDialog()
  177. } catch (e) {
  178. throw e
  179. } finally {
  180. this.loading = false
  181. }
  182. },
  183. },
  184. }
  185. </script>
  186. <style scoped>
  187. .mr-2 {
  188. margin-right: 8px;
  189. }
  190. .mb-2 {
  191. margin-bottom: 8px;
  192. }
  193. .mt-1 {
  194. margin-top: 4px;
  195. }
  196. .mt-2 {
  197. margin-top: 8px;
  198. }
  199. </style>