PageSourceSearch

https://autowhisper.xyz/assets/controllers/dashboard_modals_controller-c982baa1.js

js autowhisper.xyz collected 2026-10-02 01:11:14 UTC 7,576 bytes, 282 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3// 对应 app/views/dashboard/_modals.html.erb
4export default class extends Controller {
5  static targets = [
6    "productCreateModal", "productEditModal", "productDeleteModal",
7    "materialCreateModal", "materialDeleteModal",
8    "voiceModal",
9    "backdrop"
10  ]
11  static values = {
12    redirectUrl: String,
13    currentProductId: Number,
14    currentMaterialId: Number,
15    deleteProductConfirm: String
16  }
17
18  connect() {
19    console.log("Dashboard modals controller connected")
20    this.setupKeyboardHandlers()
21  }
22
23  disconnect() {
24    this.removeKeyboardHandlers()
25  }
26
27  // 设置键盘事件监听
28  setupKeyboardHandlers() {
29    this.boundHandleKeydown = this.handleKeydown.bind(this)
30    document.addEventListener('keydown', this.boundHandleKeydown)
31  }
32
33  // 移除键盘事件监听
34  removeKeyboardHandlers() {
35    if (this.boundHandleKeydown) {
36      document.removeEventListener('keydown', this.boundHandleKeydown)
37    }
38  }
39
40  // 处理键盘事件
41  handleKeydown(event) {
42    if (event.key === 'Escape') {
43      this.closeAllModals()
44    }
45  }
46
47  // 关闭所有模态框
48  closeAllModals() {
49    // 产品相关模态框
50    if (this.hasProductCreateModalTarget) this.hideModal(this.productCreateModalTarget)
51    if (this.hasProductEditModalTarget) this.hideModal(this.productEditModalTarget)
52    if (this.hasProductDeleteModalTarget) this.hideModal(this.productDeleteModalTarget)
53    
54    // 材料相关模态框
55    if (this.hasMaterialCreateModalTarget) this.hideModal(this.materialCreateModalTarget)
56    if (this.hasMaterialDeleteModalTarget) this.hideModal(this.materialDeleteModalTarget)
57    
58    // 语音模态框
59    if (this.hasVoiceModalTarget) this.hideModal(this.voiceModalTarget)
60  }
61
62  // 显示模态框的通用方法
63  showModal(modalTarget) {
64    if (modalTarget) {
65      modalTarget.classList.remove('hidden')
66      modalTarget.classList.add('flex')
67      
68      // 自动聚焦到第一个输入框
69      const firstInput = modalTarget.querySelector('input, textarea')
70      if (firstInput) {
71        setTimeout(() => firstInput.focus(), 100)
72      }
73    }
74  }
75
76  // 隐藏模态框的通用方法
77  hideModal(modalTarget) {
78    if (modalTarget) {
79      modalTarget.classList.add('hidden')
80      modalTarget.classList.remove('flex')
81      
82      // 清理表单
83      const form = modalTarget.querySelector('form')
84      if (form) {
85        form.reset()
86        this.clearFormErrors(form)
87      }
88    }
89  }
90
91  // 清理表单错误
92  clearFormErrors(form) {
93    // 移除错误样式
94    const errorInputs = form.querySelectorAll('.border-danger-300')
95    errorInputs.forEach(input => {
96      input.classList.remove('border-danger-300')
97      input.classList.add('border-surface-300')
98    })
99    
100    // 移除错误消息
101    const errorMessages = form.querySelectorAll('.text-danger-600')
102    errorMessages.forEach(msg => msg.remove())
103  }
104
105  // === 产品相关模态框方法 ===
106
107  // 显示创建产品模态框
108  showProductCreateModal() {
109    if (this.hasProductCreateModalTarget) {
110      this.showModal(this.productCreateModalTarget)
111    }
112  }
113
114  // 隐藏创建产品模态框
115  hideProductCreateModal() {
116    if (this.hasProductCreateModalTarget) {
117      this.hideModal(this.productCreateModalTarget)
118    }
119  }
120
121  // 显示编辑产品模态框
122  showProductEditModal(productId, productName) {
123    this.currentProductIdValue = productId
124    
125    if (this.hasProductEditModalTarget) {
126      // 填充表单数据
127      const nameInput = this.productEditModalTarget.querySelector('input[name="product[name]"]')
128      if (nameInput) nameInput.value = productName
129      
130      this.showModal(this.productEditModalTarget)
131    }
132  }
133
134  // 隐藏编辑产品模态框
135  hideProductEditModal() {
136    if (this.hasProductEditModalTarget) {
137      this.hideModal(this.productEditModalTarget)
138    }
139  }
140
141  // 显示删除产品确认模态框
142  showProductDeleteModal(productId, productName) {
143    this.currentProductIdValue = productId
144    
145    if (this.hasProductDeleteModalTarget) {
146      // 更新确认消息
147      const confirmMessage = this.productDeleteModalTarget.querySelector('[data-confirm-message]')
148      if (confirmMessage) {
149        confirmMessage.textContent = this.deleteProductConfirmValue.replace('%{name}', productName)
150      }
151      
152      this.showModal(this.productDeleteModalTarget)
153    }
154  }
155
156  // 隐藏删除产品确认模态框
157  hideProductDeleteModal() {
158    if (this.hasProductDeleteModalTarget) {
159      this.hideModal(this.productDeleteModalTarget)
160    }
161  }
162
163  // === 材料相关模态框方法 ===
164
165  // 显示创建材料模态框
166  showMaterialCreateModal(productId) {
167    this.currentProductIdValue = productId
168    
169    if (this.hasMaterialCreateModalTarget) {
170      this.showModal(this.materialCreateModalTarget)
171    }
172  }
173
174  // 隐藏创建材料模态框
175  hideMaterialCreateModal() {
176    if (this.hasMaterialCreateModalTarget) {
177      this.hideModal(this.materialCreateModalTarget)
178    }
179  }
180
181  // 显示删除材料确认模态框
182  showMaterialDeleteModal(materialId) {
183    this.currentMaterialIdValue = materialId
184    
185    if (this.hasMaterialDeleteModalTarget) {
186      this.showModal(this.materialDeleteModalTarget)
187    }
188  }
189
190  // 隐藏删除材料确认模态框
191  hideMaterialDeleteModal() {
192    if (this.hasMaterialDeleteModalTarget) {
193      this.hideModal(this.materialDeleteModalTarget)
194    }
195  }
196
197  // === 语音模态框方法 ===
198
199  // 显示语音设置模态框
200  showVoiceModal() {
201    if (this.hasVoiceModalTarget) {
202      this.showModal(this.voiceModalTarget)
203    }
204  }
205
206  // 隐藏语音设置模态框
207  hideVoiceModal() {
208    if (this.hasVoiceModalTarget) {
209      this.hideModal(this.voiceModalTarget)
210    }
211  }
212
213  // === 表单提交处理 ===
214
215  // 处理表单提交结束事件
216  handleSubmitEnd(event) {
217    const response = event.detail.fetchResponse
218    const form = event.target
219    
220    if (response.ok) {
221      // 成功时关闭相关模态框并重定向
222      this.closeModalByForm(form)
223      
224      if (this.redirectUrlValue) {
225        window.location.href = this.redirectUrlValue
226      }
227    } else {
228      // 失败时显示错误信息
229      this.handleFormErrors(form, response)
230    }
231  }
232
233  // 根据表单关闭对应的模态框
234  closeModalByForm(form) {
235    const modal = form.closest('[data-dashboard-modals-target]')
236    if (modal) {
237      this.hideModal(modal)
238    }
239  }
240
241  // 处理表单错误
242  async handleFormErrors(form, response) {
243    try {
244      const data = await response.json()
245      
246      if (data.errors) {
247        this.displayFormErrors(form, data.errors)
248      }
249    } catch (error) {
250      console.error('Failed to parse error response:', error)
251    }
252  }
253
254  // 显示表单错误
255  displayFormErrors(form, errors) {
256    // 先清理之前的错误
257    this.clearFormErrors(form)
258    
259    Object.keys(errors).forEach(field => {
260      const input = form.querySelector(`[name*="[${field}]"]`)
261      if (input) {
262        // 添加错误样式
263        input.classList.remove('border-surface-300')
264        input.classList.add('border-danger-300')
265        
266        // 添加错误消息
267        const errorMessage = document.createElement('p')
268        errorMessage.className = 'mt-1 text-sm text-danger-600'
269        errorMessage.textContent = errors[field][0]
270        
271        input.parentNode.appendChild(errorMessage)
272      }
273    })
274  }
275
276  // 点击背景关闭模态框
277  clickOutside(event) {
278    if (event.target === event.currentTarget) {
279      this.closeAllModals()
280    }
281  }
282} 

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.