DmitrMakeev commited on
Commit
cfca93e
·
verified ·
1 Parent(s): fb7a8dd

Update calculate.html

Browse files
Files changed (1) hide show
  1. calculate.html +839 -605
calculate.html CHANGED
@@ -1,628 +1,862 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
- <meta charset="UTF-8" />
5
- <title>Fertilizer Calculator</title>
6
- <!-- Example using Google Fonts -->
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link
10
- href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap"
11
- rel="stylesheet"
12
- />
13
- <style>
14
- /* Base styling */
15
- * {
16
- box-sizing: border-box;
17
- margin: 0;
18
- padding: 0;
19
- font-family: 'Open Sans', sans-serif;
20
- }
21
- body {
22
- background: linear-gradient(to top right, #e0f7fa 0%, #e1f5fe 100%);
23
- color: #333;
24
- padding: 2rem;
25
- }
26
- h1, h2, h3 {
27
- margin-bottom: 0.5rem;
28
- }
29
- h1 {
30
- font-weight: 600;
31
- text-align: center;
32
- margin-bottom: 1.5rem;
33
- }
34
-
35
- /* Container */
36
- .container {
37
- max-width: 900px;
38
- margin: 0 auto;
39
- background: #fff;
40
- border-radius: 8px;
41
- box-shadow: 0 4px 12px rgba(0,0,0,0.15);
42
- padding: 2rem;
43
- }
44
-
45
- /* Section title */
46
- .section-title {
47
- margin-top: 2rem;
48
- margin-bottom: 1rem;
49
- font-size: 1.1rem;
50
- font-weight: 600;
51
- color: #444;
52
- border-bottom: 2px solid #ddd;
53
- padding-bottom: 0.5rem;
54
- }
55
-
56
- /* Form styling */
57
- form {
58
- display: flex;
59
- flex-direction: column;
60
- }
61
- .form-group {
62
- margin-bottom: 1rem;
63
- }
64
- .label-row {
65
- display: flex;
66
- flex-wrap: wrap;
67
- justify-content: space-between;
68
- align-items: baseline;
69
- margin-bottom: 0.3rem;
70
- }
71
- .label-row label {
72
- font-weight: 600;
73
- margin-right: 0.5rem;
74
- }
75
- .label-row .param-note {
76
- font-size: 0.85rem;
77
- color: #777;
78
- flex: 1 0 auto;
79
- margin-left: 0.5rem;
80
- }
81
- input[type="number"] {
82
- width: 100%;
83
- max-width: 300px;
84
- padding: 0.5rem;
85
- border: 1px solid #ccc;
86
- border-radius: 4px;
87
- font-size: 1rem;
88
- }
89
- input[type="number"]:focus {
90
- outline: none;
91
- border-color: #007BFF;
92
- }
93
- .description {
94
- font-size: 0.85rem;
95
- color: #555;
96
- margin-top: 0.3rem;
97
- line-height: 1.4;
98
- }
99
-
100
- /* Buttons */
101
- .btn-container {
102
- margin-top: 1.5rem;
103
- }
104
- .btn {
105
- display: inline-block;
106
- background: #007BFF;
107
- color: #fff;
108
- text-decoration: none;
109
- padding: 0.6rem 1rem;
110
- border-radius: 4px;
111
- font-size: 1rem;
112
- border: none;
113
- cursor: pointer;
114
- margin-right: 1rem;
115
- transition: background 0.2s ease-in-out;
116
- }
117
- .btn:hover {
118
- background: #0056b3;
119
- }
120
- .btn.reset {
121
- background: #6c757d;
122
- }
123
- .btn.reset:hover {
124
- background: #565e64;
125
- }
126
-
127
- /* Error messages */
128
- .error {
129
- color: #dc3545;
130
- font-size: 0.85rem;
131
- margin-top: 0.25rem;
132
- display: none;
133
- }
134
- .error.show {
135
- display: block;
136
- }
137
-
138
- /* Results area */
139
- .results {
140
- margin-top: 2rem;
141
- padding: 1.5rem;
142
- background: #fafafa;
143
- border-left: 4px solid #007BFF;
144
- display: none;
145
- border-radius: 4px;
146
- }
147
- .results p {
148
- margin: 0.5rem 0;
149
- font-size: 1rem;
150
- }
151
- .results strong {
152
- color: #007BFF;
153
- }
154
- </style>
155
- </head>
156
- <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
- <h1>Fertilizer Calculator</h1>
 
 
159
 
160
- <div class="container">
161
- <form id="fertilizerForm">
162
- <!-- General Inputs -->
163
- <div class="section-title">General Inputs</div>
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
- <!-- Target Yield (Y) -->
166
- <div class="form-group">
167
- <div class="label-row">
168
- <label for="yield">Target Yield (Y):</label>
169
- <span class="param-note">(e.g., 50)</span>
170
- </div>
171
- <input
172
- type="number"
173
- id="yield"
174
- name="yield"
175
- step="0.01"
176
- min="0"
177
- placeholder="Enter target yield in kg/ha"
178
- required
179
- />
180
- <div class="description">
181
- The target yield (Y) is your desired crop yield in kg/ha (or another standard unit).
182
- </div>
183
- <div class="error" id="yieldError">
184
- Please enter a realistic yield &gt; 0 (e.g., 10 to 200).
185
- </div>
186
- </div>
187
 
188
- <!-- Nitrogen (N) Inputs -->
189
- <div class="section-title">Nitrogen (N) Parameters</div>
 
 
190
 
191
- <div class="form-group">
192
- <div class="label-row">
193
- <label for="nrN">N Required Ratio (NR<sub>N</sub>):</label>
194
- <span class="param-note">(e.g., 2)</span>
195
- </div>
196
- <input
197
- type="number"
198
- id="nrN"
199
- name="nrN"
200
- step="0.01"
201
- min="0"
202
- placeholder="N ratio for target yield"
203
- required
204
- />
205
- <div class="description">
206
- NR<sub>N</sub> represents how much nitrogen is typically required per unit of yield. Ranges often fall between 0.5 – 3.0 depending on the crop.
207
- </div>
208
- <div class="error" id="nrNError">
209
- Please enter a realistic value &gt; 0 (e.g., between 0.1 and 5).
210
- </div>
211
- </div>
212
 
213
- <div class="form-group">
214
- <div class="label-row">
215
- <label for="efN">N Efficiency Factor (E<sub>fN</sub>):</label>
216
- <span class="param-note">(0 &lt;= E<sub>fN</sub> &lt;= 1)</span>
217
- </div>
218
- <input
219
- type="number"
220
- id="efN"
221
- name="efN"
222
- step="0.01"
223
- min="0"
224
- max="1"
225
- placeholder="Between 0 and 1"
226
- required
227
- />
228
- <div class="description">
229
- E<sub>fN</sub> indicates the efficiency of nitrogen uptake, influenced by soil conditions, climate, etc.
230
- </div>
231
- <div class="error" id="efNError">
232
- N Efficiency Factor must be between 0 and 1 (e.g., 0.3, 0.45, 0.8).
233
- </div>
234
- </div>
235
 
236
- <div class="form-group">
237
- <div class="label-row">
238
- <label for="esN">Soil Contribution Factor (E<sub>sN</sub>):</label>
239
- <span class="param-note">(0 &lt;= E<sub>sN</sub> &lt;= 1)</span>
240
- </div>
241
- <input
242
- type="number"
243
- id="esN"
244
- name="esN"
245
- step="0.01"
246
- min="0"
247
- max="1"
248
- placeholder="Between 0 and 1"
249
- required
250
- />
251
- <div class="description">
252
- E<sub>sN</sub> is how much your soil naturally contributes to nitrogen availability (e.g., 0.2).
253
- </div>
254
- <div class="error" id="esNError">
255
- Soil Contribution Factor must be between 0 and 1.
256
- </div>
257
- </div>
258
 
259
- <div class="form-group">
260
- <div class="label-row">
261
- <label for="snN">N Soil Test Value (SN<sub>N</sub>):</label>
262
- <span class="param-note">(e.g., 20)</span>
263
- </div>
264
- <input
265
- type="number"
266
- id="snN"
267
- name="snN"
268
- step="0.01"
269
- min="0"
270
- placeholder="N soil test value"
271
- required
272
- />
273
- <div class="description">
274
- SN<sub>N</sub> is your nitrogen soil test result (e.g., 15 – 40). It helps determine how much N is already available.
275
- </div>
276
- <div class="error" id="snNError">
277
- Please enter a realistic soil test value &ge; 0.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  </div>
279
- </div>
280
 
281
- <!-- Phosphorus (P) Inputs -->
282
- <div class="section-title">Phosphorus (P) Parameters</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
 
284
- <div class="form-group">
285
- <div class="label-row">
286
- <label for="nrP">P Required Ratio (NR<sub>P</sub>):</label>
287
- <span class="param-note">(e.g., 0.6)</span>
288
- </div>
289
- <input
290
- type="number"
291
- id="nrP"
292
- name="nrP"
293
- step="0.01"
294
- min="0"
295
- placeholder="P ratio for target yield"
296
- required
297
- />
298
- <div class="description">
299
- NR<sub>P</sub> is the phosphorus ratio per yield unit, typically ranges from 0.1 – 1.0 depending on crop demands.
300
- </div>
301
- <div class="error" id="nrPError">
302
- Please enter a realistic value &gt; 0 (e.g., 0.1 to 2).
303
- </div>
304
- </div>
305
 
306
- <div class="form-group">
307
- <div class="label-row">
308
- <label for="efP">P Efficiency Factor (E<sub>fP</sub>):</label>
309
- <span class="param-note">(0 &lt;= E<sub>fP</sub> &lt;= 1)</span>
310
- </div>
311
- <input
312
- type="number"
313
- id="efP"
314
- name="efP"
315
- step="0.01"
316
- min="0"
317
- max="1"
318
- placeholder="Between 0 and 1"
319
- required
320
- />
321
- <div class="description">
322
- E<sub>fP</sub> indicates how effectively phosphorus is taken up by crops under your conditions.
323
- </div>
324
- <div class="error" id="efPError">
325
- P Efficiency Factor must be between 0 and 1.
326
- </div>
327
- </div>
328
 
329
- <div class="form-group">
330
- <div class="label-row">
331
- <label for="esP">Soil Contribution Factor (E<sub>sP</sub>):</label>
332
- <span class="param-note">(0 &lt;= E<sub>sP</sub> &lt;= 1)</span>
333
- </div>
334
- <input
335
- type="number"
336
- id="esP"
337
- name="esP"
338
- step="0.01"
339
- min="0"
340
- max="1"
341
- placeholder="Between 0 and 1"
342
- required
343
- />
344
- <div class="description">
345
- E<sub>sP</sub> is how much phosphorus your soil can naturally supply.
346
- </div>
347
- <div class="error" id="esPError">
348
- Soil Contribution Factor must be between 0 and 1.
349
- </div>
350
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
 
352
- <div class="form-group">
353
- <div class="label-row">
354
- <label for="snP">P Soil Test Value (SN<sub>P</sub>):</label>
355
- <span class="param-note">(e.g., 15)</span>
356
- </div>
357
- <input
358
- type="number"
359
- id="snP"
360
- name="snP"
361
- step="0.01"
362
- min="0"
363
- placeholder="P soil test value"
364
- required
365
- />
366
- <div class="description">
367
- SN<sub>P</sub> is your soil test reading for phosphorus (e.g., 5 – 30), guiding how much P is already present.
368
- </div>
369
- <div class="error" id="snPError">
370
- Please enter a realistic soil test value &ge; 0.
371
- </div>
372
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
 
374
- <!-- Potassium (K) Inputs -->
375
- <div class="section-title">Potassium (K) Parameters</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
 
377
- <div class="form-group">
378
- <div class="label-row">
379
- <label for="nrK">K Required Ratio (NR<sub>K</sub>):</label>
380
- <span class="param-note">(e.g., 1)</span>
381
- </div>
382
- <input
383
- type="number"
384
- id="nrK"
385
- name="nrK"
386
- step="0.01"
387
- min="0"
388
- placeholder="K ratio for target yield"
389
- required
390
- />
391
- <div class="description">
392
- NR<sub>K</sub> is the potassium ratio per yield unit, which could be from 0.3 2.0 for many crops.
393
- </div>
394
- <div class="error" id="nrKError">
395
- Please enter a realistic value &gt; 0 (e.g., 0.3 to 5).
396
- </div>
397
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
 
399
- <div class="form-group">
400
- <div class="label-row">
401
- <label for="efK">K Efficiency Factor (E<sub>fK</sub>):</label>
402
- <span class="param-note">(0 &lt;= E<sub>fK</sub> &lt;= 1)</span>
403
- </div>
404
- <input
405
- type="number"
406
- id="efK"
407
- name="efK"
408
- step="0.01"
409
- min="0"
410
- max="1"
411
- placeholder="Between 0 and 1"
412
- required
413
- />
414
- <div class="description">
415
- E<sub>fK</sub> indicates how efficiently potassium is used by your crops under local conditions.
416
- </div>
417
- <div class="error" id="efKError">
418
- K Efficiency Factor must be between 0 and 1.
419
- </div>
420
- </div>
421
 
422
- <div class="form-group">
423
- <div class="label-row">
424
- <label for="esK">Soil Contribution Factor (E<sub>sK</sub>):</label>
425
- <span class="param-note">(0 &lt;= E<sub>sK</sub> &lt;= 1)</span>
426
- </div>
427
- <input
428
- type="number"
429
- id="esK"
430
- name="esK"
431
- step="0.01"
432
- min="0"
433
- max="1"
434
- placeholder="Between 0 and 1"
435
- required
436
- />
437
- <div class="description">
438
- E<sub>sK</sub> is the natural potassium supply capacity of your soil.
439
- </div>
440
- <div class="error" id="esKError">
441
- Soil Contribution Factor must be between 0 and 1.
442
- </div>
443
- </div>
444
 
445
- <div class="form-group">
446
- <div class="label-row">
447
- <label for="snK">K Soil Test Value (SN<sub>K</sub>):</label>
448
- <span class="param-note">(e.g., 40)</span>
449
- </div>
450
- <input
451
- type="number"
452
- id="snK"
453
- name="snK"
454
- step="0.01"
455
- min="0"
456
- placeholder="K soil test value"
457
- required
458
- />
459
- <div class="description">
460
- SN<sub>K</sub> is your soil test reading for potassium (e.g., 30 – 100).
461
- </div>
462
- <div class="error" id="snKError">
463
- Please enter a realistic soil test value &ge; 0.
464
- </div>
465
- </div>
466
-
467
- <div class="btn-container">
468
- <button type="button" class="btn" id="calculateBtn">Calculate</button>
469
- <button type="reset" class="btn reset" id="resetBtn">Reset</button>
470
- </div>
471
- </form>
472
-
473
- <div class="results" id="resultsBox">
474
- <h2>Calculation Results</h2>
475
- <p><strong>Nitrogen (N) Fertilizer Needed:</strong> <span id="nResult"></span> kg/ha</p>
476
- <p><strong>Phosphorus (P) Fertilizer Needed:</strong> <span id="pResult"></span> kg/ha</p>
477
- <p><strong>Potassium (K) Fertilizer Needed:</strong> <span id="kResult"></span> kg/ha</p>
478
- </div>
479
- </div>
480
-
481
- <script>
482
- // Grab elements
483
- const form = document.getElementById('fertilizerForm');
484
- const calculateBtn = document.getElementById('calculateBtn');
485
- const resultsBox = document.getElementById('resultsBox');
486
-
487
- // General
488
- const yieldInput = document.getElementById('yield');
489
- const yieldError = document.getElementById('yieldError');
490
-
491
- // Nitrogen
492
- const nrN = document.getElementById('nrN');
493
- const nrNError = document.getElementById('nrNError');
494
- const efN = document.getElementById('efN');
495
- const efNError = document.getElementById('efNError');
496
- const esN = document.getElementById('esN');
497
- const esNError = document.getElementById('esNError');
498
- const snN = document.getElementById('snN');
499
- const snNError = document.getElementById('snNError');
500
-
501
- // Phosphorus
502
- const nrP = document.getElementById('nrP');
503
- const nrPError = document.getElementById('nrPError');
504
- const efP = document.getElementById('efP');
505
- const efPError = document.getElementById('efPError');
506
- const esP = document.getElementById('esP');
507
- const esPError = document.getElementById('esPError');
508
- const snP = document.getElementById('snP');
509
- const snPError = document.getElementById('snPError');
510
-
511
- // Potassium
512
- const nrK = document.getElementById('nrK');
513
- const nrKError = document.getElementById('nrKError');
514
- const efK = document.getElementById('efK');
515
- const efKError = document.getElementById('efKError');
516
- const esK = document.getElementById('esK');
517
- const esKError = document.getElementById('esKError');
518
- const snK = document.getElementById('snK');
519
- const snKError = document.getElementById('snKError');
520
-
521
- // Results
522
- const nResult = document.getElementById('nResult');
523
- const pResult = document.getElementById('pResult');
524
- const kResult = document.getElementById('kResult');
525
-
526
- // Basic front-end validations
527
- function validateInputs() {
528
- let valid = true;
529
-
530
- // A function to show/hide error
531
- const showError = (input, errorEl, condition) => {
532
- if (condition) {
533
- errorEl.classList.add('show');
534
- valid = false;
535
- } else {
536
- errorEl.classList.remove('show');
537
- }
538
- };
539
-
540
- // Typical yield range check (example: 1–300)
541
- const yVal = Number(yieldInput.value);
542
- showError(yieldInput, yieldError, (yVal <= 0 || yVal > 300));
543
-
544
- // N checks
545
- const nrNVal = Number(nrN.value);
546
- showError(nrN, nrNError, (nrNVal <= 0 || nrNVal > 5));
547
- const efNVal = Number(efN.value);
548
- showError(efN, efNError, (efNVal < 0 || efNVal > 1));
549
- const esNVal = Number(esN.value);
550
- showError(esN, esNError, (esNVal < 0 || esNVal > 1));
551
- const snNVal = Number(snN.value);
552
- showError(snN, snNError, (snNVal < 0 || snNVal > 500)); // example soil test upper limit
553
-
554
- // P checks
555
- const nrPVal = Number(nrP.value);
556
- showError(nrP, nrPError, (nrPVal <= 0 || nrPVal > 2));
557
- const efPVal = Number(efP.value);
558
- showError(efP, efPError, (efPVal < 0 || efPVal > 1));
559
- const esPVal = Number(esP.value);
560
- showError(esP, esPError, (esPVal < 0 || esPVal > 1));
561
- const snPVal = Number(snP.value);
562
- showError(snP, snPError, (snPVal < 0 || snPVal > 500));
563
-
564
- // K checks
565
- const nrKVal = Number(nrK.value);
566
- showError(nrK, nrKError, (nrKVal <= 0 || nrKVal > 5));
567
- const efKVal = Number(efK.value);
568
- showError(efK, efKError, (efKVal < 0 || efKVal > 1));
569
- const esKVal = Number(esK.value);
570
- showError(esK, esKError, (esKVal < 0 || esKVal > 1));
571
- const snKVal = Number(snK.value);
572
- showError(snK, snKError, (snKVal < 0 || snKVal > 1000)); // example upper limit
573
-
574
- return valid;
575
- }
576
-
577
- // Example formula usage (client-side for demo):
578
- // F_N = (NR_N / E_fN) * Y - (E_sN / E_fN) * SN_N
579
- // Similarly for P, K with their respective parameters.
580
-
581
- function calculateFertilizer() {
582
- // Convert to numeric
583
- const Y = Number(yieldInput.value);
584
-
585
- // N
586
- const NR_N = Number(nrN.value);
587
- const EfN = Number(efN.value);
588
- const EsN = Number(esN.value);
589
- const SN_N = Number(snN.value);
590
- const F_N = (NR_N / EfN) * Y - (EsN / EfN) * SN_N;
591
-
592
- // P
593
- const NR_P = Number(nrP.value);
594
- const EfP = Number(efP.value);
595
- const EsP = Number(esP.value);
596
- const SN_P = Number(snP.value);
597
- const F_P = (NR_P / EfP) * Y - (EsP / EfP) * SN_P;
598
-
599
- // K
600
- const NR_K = Number(nrK.value);
601
- const EfK = Number(efK.value);
602
- const EsK = Number(esK.value);
603
- const SN_K = Number(snK.value);
604
- const F_K = (NR_K / EfK) * Y - (EsK / EfK) * SN_K;
605
-
606
- // Display results
607
- nResult.textContent = F_N.toFixed(2);
608
- pResult.textContent = F_P.toFixed(2);
609
- kResult.textContent = F_K.toFixed(2);
610
- resultsBox.style.display = 'block';
611
- }
612
-
613
- calculateBtn.addEventListener('click', () => {
614
- // Validate inputs before calculation
615
- if (!validateInputs()) {
616
- return;
617
- }
618
- // In production, you'd do a backend call here. We'll do client-side for demonstration.
619
- calculateFertilizer();
620
- });
621
-
622
- // Optionally hide results on reset
623
- document.getElementById('resetBtn').addEventListener('click', () => {
624
- resultsBox.style.display = 'none';
625
- });
626
- </script>
627
  </body>
628
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="zh-TW">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>營養液成分計算機</title>
7
+ <style>
8
+ .container {
9
+ max-width: 800px;
10
+ margin: 0 auto;
11
+ padding: 20px;
12
+ }
13
+ .fertilizer-list {
14
+ margin-top: 20px;
15
+ border: 1px solid #e0e0e0;
16
+ padding: 20px;
17
+ border-radius: 12px;
18
+ background-color: #ffffff;
19
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
20
+ }
21
+ .fertilizer-list h2 {
22
+ margin-top: 0;
23
+ color: #2c3e50;
24
+ border-bottom: 2px solid #eef2f7;
25
+ padding-bottom: 12px;
26
+ margin-bottom: 20px;
27
+ font-size: 1.4em;
28
+ }
29
+ .fertilizer-list ul {
30
+ list-style: none;
31
+ padding: 0;
32
+ margin: 0;
33
+ }
34
+ .fertilizer-list li {
35
+ display: flex;
36
+ justify-content: space-between;
37
+ align-items: center;
38
+ padding: 12px 16px;
39
+ margin-bottom: 10px;
40
+ background-color: #f8fafc;
41
+ border: 1px solid #edf2f7;
42
+ border-radius: 8px;
43
+ transition: all 0.2s ease;
44
+ }
45
+ .fertilizer-list li:hover {
46
+ background-color: #edf2f7;
47
+ transform: translateX(2px);
48
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
49
+ }
50
+ .fertilizer-list .fertilizer-info {
51
+ flex-grow: 1;
52
+ display: flex;
53
+ justify-content: space-between;
54
+ align-items: center;
55
+ padding-right: 24px;
56
+ min-width: 0;
57
+ gap: 16px;
58
+ }
59
+ .fertilizer-list .formula {
60
+ font-weight: 500;
61
+ color: #2d3748;
62
+ flex: 1;
63
+ white-space: nowrap;
64
+ overflow: hidden;
65
+ text-overflow: ellipsis;
66
+ font-size: 0.95em;
67
+ }
68
+ .fertilizer-list .amount {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 8px;
72
+ min-width: 120px;
73
+ justify-content: flex-end;
74
+ }
75
+ .fertilizer-list .amount-display {
76
+ cursor: pointer;
77
+ padding: 4px 8px;
78
+ border-radius: 4px;
79
+ background-color: #edf2f7;
80
+ color: #4a5568;
81
+ font-family: monospace;
82
+ transition: all 0.2s ease;
83
+ }
84
+ .fertilizer-list .amount-display:hover {
85
+ background-color: #e2e8f0;
86
+ }
87
+ .fertilizer-list .amount-input {
88
+ width: 100px;
89
+ padding: 4px 8px;
90
+ border: 1px solid #cbd5e0;
91
+ border-radius: 4px;
92
+ text-align: right;
93
+ font-family: monospace;
94
+ }
95
+ .fertilizer-list .edit-controls {
96
+ display: flex;
97
+ gap: 4px;
98
+ }
99
+ .fertilizer-list .edit-btn {
100
+ padding: 4px 8px;
101
+ border: none;
102
+ border-radius: 4px;
103
+ cursor: pointer;
104
+ font-size: 14px;
105
+ transition: all 0.2s ease;
106
+ }
107
+ .fertilizer-list .save-btn {
108
+ background-color: #48bb78;
109
+ color: white;
110
+ }
111
+ .fertilizer-list .save-btn:hover {
112
+ background-color: #38a169;
113
+ }
114
+ .fertilizer-list .cancel-btn {
115
+ background-color: #a0aec0;
116
+ color: white;
117
+ }
118
+ .fertilizer-list .cancel-btn:hover {
119
+ background-color: #718096;
120
+ }
121
+ .fertilizer-list .remove-btn {
122
+ background-color: #fc8181;
123
+ color: white;
124
+ border: none;
125
+ padding: 6px 12px;
126
+ border-radius: 6px;
127
+ cursor: pointer;
128
+ transition: all 0.2s ease;
129
+ font-size: 0.9em;
130
+ }
131
+ .fertilizer-list .remove-btn:hover {
132
+ background-color: #f56565;
133
+ }
134
+ .no-fertilizers {
135
+ text-align: center;
136
+ color: #718096;
137
+ font-style: italic;
138
+ padding: 24px;
139
+ background-color: #f7fafc;
140
+ border-radius: 8px;
141
+ border: 1px dashed #cbd5e0;
142
+ }
143
+ .result-table {
144
+ width: 100%;
145
+ border-collapse: collapse;
146
+ margin-top: 20px;
147
+ }
148
+ .result-table th, .result-table td {
149
+ border: 1px solid #ccc;
150
+ padding: 8px;
151
+ text-align: center;
152
+ }
153
+ .fertilizer-selector select {
154
+ width: 100%;
155
+ margin: 10px 0;
156
+ max-height: 200px;
157
+ }
158
+
159
+ #fertilizer-search {
160
+ width: 100%;
161
+ padding: 8px;
162
+ margin-bottom: 5px;
163
+ border: 1px solid #ccc;
164
+ border-radius: 4px;
165
+ }
166
+ .test-btn {
167
+ background-color: #3498db;
168
+ color: white;
169
+ border: none;
170
+ padding: 8px 15px;
171
+ border-radius: 4px;
172
+ cursor: pointer;
173
+ margin-bottom: 10px;
174
+ transition: background-color 0.2s;
175
+ position: relative;
176
+ }
177
 
178
+ .test-btn:hover {
179
+ background-color: #2980b9;
180
+ }
181
 
182
+ .test-btn .tooltip {
183
+ visibility: hidden;
184
+ width: 120px;
185
+ background-color: #555;
186
+ color: #fff;
187
+ text-align: center;
188
+ border-radius: 6px;
189
+ padding: 5px 0;
190
+ position: absolute;
191
+ z-index: 1;
192
+ bottom: 125%; /* Position the tooltip above the button */
193
+ left: 50%;
194
+ margin-left: -60px;
195
+ opacity: 0;
196
+ transition: opacity 0.3s;
197
+ }
198
 
199
+ .test-btn .tooltip::after {
200
+ content: "";
201
+ position: absolute;
202
+ top: 100%; /* At the bottom of the tooltip */
203
+ left: 50%;
204
+ margin-left: -5px;
205
+ border-width: 5px;
206
+ border-style: solid;
207
+ border-color: #555 transparent transparent transparent;
208
+ }
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
+ .test-btn:hover .tooltip {
211
+ visibility: visible;
212
+ opacity: 1;
213
+ }
214
 
215
+ /* 添加導航按鈕樣式 */
216
+ .nav-button {
217
+ display: none;
218
+ padding: 8px 16px;
219
+ background-color: #4299e1;
220
+ color: white;
221
+ text-decoration: none;
222
+ border-radius: 6px;
223
+ transition: all 0.2s ease;
224
+ }
 
 
 
 
 
 
 
 
 
 
 
225
 
226
+ .nav-button:hover {
227
+ background-color: #3182ce;
228
+ transform: translateY(-1px);
229
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
230
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
 
232
+ .clear-btn {
233
+ background-color: #e53e3e;
234
+ color: white;
235
+ border: none;
236
+ padding: 8px 15px;
237
+ border-radius: 4px;
238
+ cursor: pointer;
239
+ margin-bottom: 10px;
240
+ transition: background-color 0.2s;
241
+ }
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
+ .clear-btn:hover {
244
+ background-color: #c53030;
245
+ }
246
+
247
+ #export-container {
248
+ display: none;
249
+ position: absolute;
250
+ top: 50%;
251
+ left: 50%;
252
+ transform: translate(-50%, -50%);
253
+ background-color: white;
254
+ border: 1px solid #ccc;
255
+ padding: 20px;
256
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
257
+ z-index: 1000;
258
+ }
259
+ </style>
260
+ </head>
261
+ <body>
262
+ <div class="container">
263
+ <h1>營養液成分計算機</h1>
264
+
265
+ <!-- 隱藏導航按鈕,但保留功能 -->
266
+ <a href="reverse-calculator.html" class="nav-button" style="display: none;">反向計算機</a>
267
+
268
+ <!-- 肥料選擇區域 -->
269
+ <div class="fertilizer-selector">
270
+ <h2>選擇肥料</h2>
271
+ <button id="preset-button-1" onclick="loadPreset1()" class="test-btn">
272
+ 預設配方 1
273
+ <span class="tooltip">台中農改場萵苣配方</span>
274
+ </button>
275
+ <button id="preset-button-2" onclick="loadPreset2()" class="test-btn">
276
+ 預設配方 2
277
+ <span class="tooltip">現代田園與養液栽培 p526 葉菜通用基本養液</span>
278
+ </button>
279
+ <button id="preset-button-3" onclick="loadPreset3()" class="test-btn">
280
+ 預設配方 3
281
+ <span class="tooltip">Hoagland Solution</span>
282
+ </button>
283
+ <button id="clear-button" onclick="clearFormula()" class="clear-btn">清空配方</button>
284
+ <button id="export-button" onclick="exportFormula()" class="test-btn">匯出配方</button>
285
+ <div id="export-container" style="display: none;">
286
+ <textarea id="export-textarea" readonly style="width: 100%; height: 100px; margin-top: 10px;"></textarea>
287
+ <button onclick="copyToClipboard()" class="test-btn">一鍵複製</button>
288
+ </div>
289
+ <input type="text" id="fertilizer-search" placeholder="輸入肥料名稱或分子式搜尋...">
290
+ <select id="fertilizer-select" size="5">
291
+ <option value="">請選擇肥料...</option>
292
+ </select>
293
+ <input type="number" id="amount" placeholder="輸入用量(mg/L)" step="0.1">
294
+ <button onclick="addFertilizer()">添加</button>
295
+ </div>
296
+
297
+ <!-- 已選擇肥料清單 -->
298
+ <div class="fertilizer-list">
299
+ <h2>已選擇的肥料</h2>
300
+ <ul id="selected-fertilizers"></ul>
301
+ </div>
302
+
303
+ <!-- 計算結果 -->
304
+ <div class="calculation-results">
305
+ <h2>養分含量</h2>
306
+ <table class="result-table">
307
+ <tr>
308
+ <th>元素</th>
309
+ <th>ppm</th>
310
+ <th>meq/L</th>
311
+ <th>mmol/L</th>
312
+ </tr>
313
+ <tbody id="results-body">
314
+ <!-- 結果將由JavaScript動態填充 -->
315
+ </tbody>
316
+ </table>
317
  </div>
318
+ </div>
319
 
320
+ <script>
321
+ // 添加名稱對應表
322
+ const fertilizerAliases = {
323
+ 'Fe-EDTA·3H2O': 'C10H12FeN2NaO8·3H2O',
324
+ '螯合鐵': 'C10H12FeN2NaO8·3H2O',
325
+ '酒石酸鐵': 'C12H12Fe2O18',
326
+ };
327
+
328
+ // 分子量對照表
329
+ const molecularWeights = {
330
+ // 化合物
331
+ 'Ca(NO3)2·4H2O': 236.149920, // 40.078 + 2(14.0067 + 3*15.999) + 4(18.015340)
332
+ 'KNO3': 101.102530, // 39.0983 + 14.0067 + 3*15.999
333
+ 'MgSO4·7H2O': 246.474780, // 24.305 + 32.065 + 4*15.999 + 7(18.015340)
334
+ 'NH4H2PO4': 115.025738, // 14.0067 + 4*1.007940 + 30.973762 + 4*15.999
335
+ 'Ca(H2PO4)2·H2O': 252.082802, // 40.078 + 2(2*1.007940 + 30.973762 + 4*15.999) + 18.015340
336
+ 'C10H12FeN2NaO8·3H2O': 412.147420, // Fe-EDTA·3H2O: 10*12.0107 + 12*1.007940 + 55.845 + 2*14.0067 + 22.989770 + 8*15.999 + 3(18.015340)
337
+ 'C12H12Fe2O18': 555.895680, // 12*12.0107 + 12*1.007940 + 2*55.845 + 18*15.999
338
+ 'H3BO3': 61.833220, // 3*1.007940 + 10.811 + 3*15.999
339
+ 'MnCl2·4H2O': 197.905360, // 54.938045 + 2*35.453 + 4(18.015340)
340
+ 'ZnSO4·7H2O': 287.539780, // 65.38 + 32.065 + 4*15.999 + 7(18.015340)
341
+ 'CuSO4·5H2O': 249.685700, // 63.546 + 32.065 + 4*15.999 + 5(18.015340)
342
+ 'Na2MoO4·2H2O': 241.950680, // 2*22.989770 + 95.96 + 4*15.999 + 2(18.015340)
343
+ 'KH2PO4': 136.085838, // 39.0983 + 2*1.007940 + 30.973762 + 4*15.999
344
+ 'Ca(NO3)2': 164.088400, // 40.078 + 2(14.0067 + 3*15.999)
345
+ 'MgSO4': 120.366000, // 24.305 + 32.065 + 4*15.999
346
+ '(NH4)6Mo7O24·4H2O': 1235.86, // 6(14.0067 + 4*1.007940) + 7*95.96 + 24*15.999 + 4(2*1.007940 + 15.999)
347
+ '(NH4)2MoO4': 196.0354, // (2 * (14.0067 + 4 * 1.00794)) + 95.96 + (4 * 15.999)
348
+ 'H2MoO4·H2O': 178.95068, // 2*1.00794 + 95.96 + 4*15.999 + 18.01534
349
+
350
+ // 元素
351
+ 'N': 14.006700,
352
+ 'P': 30.973762,
353
+ 'K': 39.098300,
354
+ 'Ca': 40.078000,
355
+ 'Mg': 24.305000,
356
+ 'S': 32.065000,
357
+ 'Fe': 55.845000,
358
+ 'Mn': 54.938045,
359
+ 'Zn': 65.380000,
360
+ 'Cu': 63.546000,
361
+ 'B': 10.811000,
362
+ 'Mo': 95.960000,
363
+ 'Cl': 35.453000,
364
+
365
+ // 常用計算值
366
+ 'H': 1.007940,
367
+ 'O': 15.999000,
368
+ 'Na': 22.989770,
369
+ 'C': 12.010700,
370
+ 'H2O': 18.015340 // 2*1.007940 + 15.999
371
+ };
372
+
373
+ // 肥料成分對照表(以重量比例表示)
374
+ const fertilizerComposition = {
375
+ // 大量元素肥料
376
+ 'Ca(NO3)2·4H2O': {
377
+ 'Ca': 0.169715, // 40.078 / 236.149920 = 16.97% Ca
378
+ 'N': 0.118721 // (2 * 14.0067) / 236.149920 = 11.87% N
379
+ },
380
+ 'KNO3': {
381
+ 'K': 0.386716, // 39.0983 / 101.102530 = 38.67% K
382
+ 'N': 0.138486 // 14.0067 / 101.102530 = 13.85% N
383
+ },
384
+ 'MgSO4·7H2O': {
385
+ 'Mg': 0.098611, // 24.305 / 246.474780 = 9.86% Mg
386
+ 'S': 0.130095 // 32.065 / 246.474780 = 13.01% S
387
+ },
388
+ 'NH4H2PO4': {
389
+ 'N': 0.121773, // 14.0067 / 115.025738 = 12.18% N
390
+ 'P': 0.269278, // 30.973762 / 115.025738 = 26.93% P
391
+ 'H': 0.035023 // (4 * 1.007940) / 115.025738 = 3.50% H
392
+ },
393
+ 'Ca(H2PO4)2·H2O': {
394
+ 'Ca': 0.158987, // 40.078 / 252.082802 = 15.90% Ca
395
+ 'P': 0.245848, // (2 * 30.973762) / 252.082802 = 24.58% P
396
+ 'H': 0.008000 // (4 * 1.007940) / 252.082802 = 0.80% H
397
+ },
398
+ 'KH2PO4': {
399
+ 'K': 0.287305, // 39.0983 / 136.085838 = 28.73% K
400
+ 'P': 0.227605, // 30.973762 / 136.085838 = 22.76% P
401
+ 'H': 0.014819 // (2 * 1.007940) / 136.085838 = 1.48% H
402
+ },
403
+ 'Ca(NO3)2': {
404
+ 'Ca': 0.244497, // 40.078 / 164.088400 = 24.45% Ca
405
+ 'N': 0.170744 // (2 * 14.0067) / 164.088400 = 17.07% N
406
+ },
407
+ 'MgSO4': {
408
+ 'Mg': 0.201927, // 24.305 / 120.366000 = 20.19% Mg
409
+ 'S': 0.266393 // 32.065 / 120.366000 = 26.64% S
410
+ },
411
+
412
+ // 微量元素肥料
413
+ 'C10H12FeN2NaO8·3H2O': {
414
+ 'Fe': 0.135498 // 55.845 / 412.147420 = 13.55% Fe
415
+ },
416
+ 'C12H12Fe2O18': {
417
+ 'Fe': 0.200919 // (2 * 55.845) / 555.895680 = 20.09% Fe
418
+ },
419
+ 'H3BO3': {
420
+ 'B': 0.174842 // 10.811 / 61.833220 = 17.48% B
421
+ },
422
+ 'MnCl2·4H2O': {
423
+ 'Mn': 0.277595, // 54.938045 / 197.905360 = 27.76% Mn
424
+ 'Cl': 0.358521 // (2 * 35.453) / 197.905360 = 35.85% Cl
425
+ },
426
+ 'ZnSO4·7H2O': {
427
+ 'Zn': 0.227379, // 65.38 / 287.539780 = 22.74% Zn
428
+ 'S': 0.111515 // 32.065 / 287.539780 = 11.15% S
429
+ },
430
+ 'CuSO4·5H2O': {
431
+ 'Cu': 0.254504, // 63.546 / 249.685700 = 25.45% Cu
432
+ 'S': 0.128421 // 32.065 / 249.685700 = 12.84% S
433
+ },
434
+ 'Na2MoO4·2H2O': {
435
+ 'Mo': 0.396610, // 95.96 / 241.950680 = 39.66% Mo
436
+ 'Na': 0.190037 // 2*22.989770 / 241.950680 = 19.00% Na
437
+ },
438
+ '(NH4)6Mo7O24·4H2O': {
439
+ 'N': 0.068033, // (6 * 14.0067) / 1235.86 = 6.80% N
440
+ 'Mo': 0.543754 // (7 * 95.96) / 1235.86 = 54.38% Mo
441
+ },
442
+ '(NH4)2MoO4': {
443
+ 'N': 0.142935, // (2 * 14.0067) / 196.0354 = 14.29% N
444
+ 'Mo': 0.489497 // (1 * 95.96) / 196.0354 = 48.95% Mo
445
+ },
446
+ 'H2MoO4·H2O': {
447
+ 'Mo': 0.536, // 95.96 / 178.95068 = 53.6% Mo
448
+ }
449
+ };
450
+
451
+ // 離子價數對照表(用於 meq 計算)
452
+ const ionicCharges = {
453
+ 'N': -1, // NO3- 形式
454
+ 'P': -1, // H2PO4- 形式
455
+ 'K': +1, // K+ 形式
456
+ 'Ca': +2, // Ca2+ 形式
457
+ 'Mg': +2, // Mg2+ 形式
458
+ 'S': -2, // SO4-2 形式
459
+ 'Fe': +2, // Fe2+ 形式
460
+ 'Mn': +2, // Mn2+ 形式
461
+ 'Zn': +2, // Zn2+ 形式
462
+ 'Cu': +2, // Cu2+ 形式
463
+ 'B': +3, // B3+ 形式
464
+ 'Mo': -2, // MoO4-2 形式
465
+ 'Cl': -1, // Cl- 形式
466
+ 'Na': +1 // Na+ 形式
467
+ };
468
+
469
+ const selectedFertilizers = [];
470
+
471
+ // 定義肥料清單
472
+ const fertilizerList = [
473
+ { value: 'Ca(NO3)2·4H2O', label: '硝酸鈣(四水) Ca(NO3)2·4H2O' },
474
+ { value: 'NH4H2PO4', label: '磷酸一銨 NH4H2PO4' },
475
+ { value: 'MgSO4·7H2O', label: '硫酸鎂(七水) MgSO4·7H2O' },
476
+ { value: 'Ca(H2PO4)2·H2O', label: '磷酸一鈣(一水) Ca(H2PO4)2·H2O' },
477
+ { value: 'Ca(NO3)2', label: '硝酸鈣 Ca(NO3)2' },
478
+ { value: 'KNO3', label: '硝酸鉀 KNO3' },
479
+ { value: 'MgSO4', label: '硫酸鎂 MgSO4' },
480
+ { value: 'KH2PO4', label: '磷酸二氫鉀 KH2PO4' },
481
+ { value: 'Fe-EDTA·3H2O', label: '螯合鐵(Fe-EDTA·3H2O)' },
482
+ { value: 'C12H12Fe2O18', label: '酒石酸鐵(C12H12Fe2O18)' },
483
+ { value: 'H3BO3', label: '硼酸 H3BO3' },
484
+ { value: 'MnCl2·4H2O', label: '氯化錳(四水) MnCl2·4H2O' },
485
+ { value: 'ZnSO4·7H2O', label: '硫酸鋅(七水) ZnSO4·7H2O' },
486
+ { value: 'CuSO4·5H2O', label: '硫酸銅(五水) CuSO4·5H2O' },
487
+ { value: 'Na2MoO4·2H2O', label: '鉬酸鈉(二水) Na2MoO4·2H2O' },
488
+ { value: '(NH4)6Mo7O24·4H2O', label: '仲鉬酸銨(四水) (NH4)6Mo7O24·4H2O' },
489
+ { value: '(NH4)2MoO4', label: '鉬酸銨 (NH4)2MoO4' },
490
+ { value: 'H2MoO4·H2O', label: '鉬酸(一水) H2MoO4·H2O' }
491
+ ];
492
+
493
+ // 初始化肥料選單
494
+ function initializeFertilizerSelect() {
495
+ const select = document.getElementById('fertilizer-select');
496
+ updateFertilizerOptions(fertilizerList);
497
+ }
498
 
499
+ // 更新肥料選項
500
+ function updateFertilizerOptions(options) {
501
+ const select = document.getElementById('fertilizer-select');
502
+ select.innerHTML = '<option value="">請選擇肥料...</option>';
503
+ options.forEach(option => {
504
+ const optElement = document.createElement('option');
505
+ optElement.value = option.value;
506
+ optElement.textContent = option.label;
507
+ select.appendChild(optElement);
508
+ });
509
+ }
 
 
 
 
 
 
 
 
 
 
510
 
511
+ // 搜尋肥料
512
+ function searchFertilizers(searchText) {
513
+ const filteredList = fertilizerList.filter(fertilizer => {
514
+ const searchLower = searchText.toLowerCase();
515
+ return fertilizer.label.toLowerCase().includes(searchLower) ||
516
+ fertilizer.value.toLowerCase().includes(searchLower);
517
+ });
518
+ updateFertilizerOptions(filteredList);
519
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
520
 
521
+ // 添加事件監聽器
522
+ document.addEventListener('DOMContentLoaded', function() {
523
+ initializeFertilizerSelect();
524
+
525
+ const searchInput = document.getElementById('fertilizer-search');
526
+ searchInput.addEventListener('input', function(e) {
527
+ searchFertilizers(e.target.value);
528
+ });
529
+
530
+ // 添加鍵盤快捷鍵支援
531
+ const select = document.getElementById('fertilizer-select');
532
+ searchInput.addEventListener('keydown', function(e) {
533
+ if (e.key === 'ArrowDown') {
534
+ e.preventDefault();
535
+ select.focus();
536
+ if (select.options.length > 0) {
537
+ select.selectedIndex = 0;
538
+ }
539
+ }
540
+ });
541
+
542
+ // 添加 Enter 鍵支援
543
+ const amountInput = document.getElementById('amount');
544
+ amountInput.addEventListener('keydown', function(e) {
545
+ if (e.key === 'Enter') {
546
+ addFertilizer();
547
+ }
548
+ });
549
+ });
550
+
551
+ function addFertilizer() {
552
+ const select = document.getElementById('fertilizer-select');
553
+ const amount = document.getElementById('amount').value;
554
+
555
+ if (select.value && amount) {
556
+ const existingIndex = selectedFertilizers.findIndex(f => f.formula === select.value);
557
+
558
+ if (existingIndex !== -1) {
559
+ // 如果存在,加上新的數量
560
+ selectedFertilizers[existingIndex].amount = roundNumber(
561
+ selectedFertilizers[existingIndex].amount + parseFloat(amount)
562
+ );
563
+ } else {
564
+ // 如果不存在,新增一條
565
+ selectedFertilizers.push({
566
+ formula: select.value,
567
+ amount: roundNumber(parseFloat(amount))
568
+ });
569
+ }
570
+
571
+ document.getElementById('amount').value = '';
572
+ updateFertilizerList();
573
+ calculateNutrients();
574
+ }
575
+ }
576
 
577
+ function updateFertilizerList() {
578
+ const list = document.getElementById('selected-fertilizers');
579
+ list.innerHTML = '';
580
+
581
+ if (selectedFertilizers.length === 0) {
582
+ const emptyMessage = document.createElement('div');
583
+ emptyMessage.className = 'no-fertilizers';
584
+ emptyMessage.textContent = '尚未添加任何肥料';
585
+ list.appendChild(emptyMessage);
586
+ return;
587
+ }
588
+
589
+ selectedFertilizers.forEach((fert, index) => {
590
+ const li = document.createElement('li');
591
+
592
+ const infoDiv = document.createElement('div');
593
+ infoDiv.className = 'fertilizer-info';
594
+
595
+ // 查找肥料的中文名稱
596
+ const fertilizerInfo = fertilizerList.find(f => f.value === fert.formula);
597
+ const displayName = fertilizerInfo ? fertilizerInfo.label : fert.formula;
598
+
599
+ const formula = document.createElement('span');
600
+ formula.className = 'formula';
601
+ formula.textContent = displayName; // 使用中文名稱
602
+
603
+ const amountContainer = document.createElement('div');
604
+ amountContainer.className = 'amount';
605
+
606
+ // 顯示用量的元素
607
+ const amountDisplay = document.createElement('span');
608
+ amountDisplay.className = 'amount-display';
609
+ amountDisplay.textContent = `${formatNumber(fert.amount)} mg/L`;
610
+ amountDisplay.onclick = () => startEditing(amountContainer, fert, index);
611
+
612
+ amountContainer.appendChild(amountDisplay);
613
+ infoDiv.appendChild(formula);
614
+ infoDiv.appendChild(amountContainer);
615
+
616
+ const removeBtn = document.createElement('button');
617
+ removeBtn.className = 'remove-btn';
618
+ removeBtn.textContent = '移除';
619
+ removeBtn.onclick = () => {
620
+ selectedFertilizers.splice(index, 1);
621
+ updateFertilizerList();
622
+ calculateNutrients();
623
+ };
624
+
625
+ li.appendChild(infoDiv);
626
+ li.appendChild(removeBtn);
627
+ list.appendChild(li);
628
+ });
629
+ }
630
 
631
+ function startEditing(container, fert, index) {
632
+ // 清空容器
633
+ container.innerHTML = '';
634
+
635
+ // 創建輸入框
636
+ const input = document.createElement('input');
637
+ input.type = 'number';
638
+ input.className = 'amount-input';
639
+ input.value = fert.amount;
640
+ input.step = '0.001';
641
+
642
+ // 創建按鈕容器
643
+ const buttonContainer = document.createElement('div');
644
+ buttonContainer.className = 'edit-controls';
645
+
646
+ // 創建保存按鈕
647
+ const saveBtn = document.createElement('button');
648
+ saveBtn.className = 'edit-btn save-btn';
649
+ saveBtn.textContent = '✓';
650
+ saveBtn.onclick = () => {
651
+ const newAmount = roundNumber(parseFloat(input.value));
652
+ if (newAmount > 0) {
653
+ selectedFertilizers[index].amount = newAmount;
654
+ updateFertilizerList();
655
+ calculateNutrients();
656
+ }
657
+ };
658
+
659
+ // 創建取消按鈕
660
+ const cancelBtn = document.createElement('button');
661
+ cancelBtn.className = 'edit-btn cancel-btn';
662
+ cancelBtn.textContent = '✕';
663
+ cancelBtn.onclick = () => {
664
+ updateFertilizerList();
665
+ };
666
+
667
+ // 添加到容器
668
+ buttonContainer.appendChild(saveBtn);
669
+ buttonContainer.appendChild(cancelBtn);
670
+ container.appendChild(input);
671
+ container.appendChild(buttonContainer);
672
+
673
+ // 聚焦到輸入框
674
+ input.focus();
675
+ input.select();
676
+
677
+ // 添加鍵盤事件處理
678
+ input.addEventListener('keydown', (e) => {
679
+ if (e.key === 'Enter') {
680
+ saveBtn.click();
681
+ } else if (e.key === 'Escape') {
682
+ cancelBtn.click();
683
+ }
684
+ });
685
+ }
686
 
687
+ function calculateNutrients() {
688
+ const results = {
689
+ 'N': { ppm: 0, meq: 0, mmol: 0 },
690
+ 'P': { ppm: 0, meq: 0, mmol: 0 },
691
+ 'K': { ppm: 0, meq: 0, mmol: 0 },
692
+ 'Ca': { ppm: 0, meq: 0, mmol: 0 },
693
+ 'Mg': { ppm: 0, meq: 0, mmol: 0 },
694
+ 'S': { ppm: 0, meq: 0, mmol: 0 },
695
+ 'Fe': { ppm: 0, meq: 0, mmol: 0 },
696
+ 'Mn': { ppm: 0, meq: 0, mmol: 0 },
697
+ 'Zn': { ppm: 0, meq: 0, mmol: 0 },
698
+ 'Cu': { ppm: 0, meq: 0, mmol: 0 },
699
+ 'B': { ppm: 0, meq: 0, mmol: 0 },
700
+ 'Mo': { ppm: 0, meq: 0, mmol: 0 },
701
+ 'Cl': { ppm: 0, meq: 0, mmol: 0 },
702
+ 'Na': { ppm: 0, meq: 0, mmol: 0 }
703
+ };
704
+
705
+ selectedFertilizers.forEach(fert => {
706
+ // 使用實際的化學式進行查找
707
+ const actualFormula = fertilizerAliases[fert.formula] || fert.formula;
708
+ const composition = fertilizerComposition[actualFormula];
709
+
710
+ if (composition) {
711
+ for (let element in composition) {
712
+ if (results[element]) {
713
+ // 計算 ppm (mg/L)
714
+ const ppm = roundNumber(fert.amount * composition[element], 4);
715
+ results[element].ppm += ppm;
716
+
717
+ // 計算 mmol/L
718
+ const mmol = roundNumber(ppm / molecularWeights[element], 6);
719
+ results[element].mmol += mmol;
720
+
721
+ // 計算 meq/L
722
+ const meq = roundNumber(mmol * Math.abs(ionicCharges[element]), 6);
723
+ results[element].meq += meq;
724
+ }
725
+ }
726
+ }
727
+ });
728
+
729
+ // 更新結果表格
730
+ const tbody = document.getElementById('results-body');
731
+ tbody.innerHTML = '';
732
+ Object.entries(results).forEach(([element, values]) => {
733
+ const tr = document.createElement('tr');
734
+ // 在元素符號後添加離子電荷標示
735
+ const chargeSymbol = ionicCharges[element] > 0 ?
736
+ `<sup>${ionicCharges[element]}+</sup>` :
737
+ `<sup>${Math.abs(ionicCharges[element])}-</sup>`;
738
+
739
+ tr.innerHTML = `
740
+ <td>${element}${chargeSymbol}</td>
741
+ <td>${formatNumber(values.ppm, 4)}</td>
742
+ <td>${formatNumber(values.meq, 6)}</td>
743
+ <td>${formatNumber(values.mmol, 6)}</td>
744
+ `;
745
+ tbody.appendChild(tr);
746
+ });
747
+ }
748
 
749
+ function loadPreset1() {
750
+ selectedFertilizers.length = 0;
751
+
752
+ const preset1 = [
753
+ { formula: 'Ca(NO3)2·4H2O', amount: 94.0 },
754
+ { formula: 'KNO3', amount: 484.0 },
755
+ { formula: 'NH4H2PO4', amount: 62.0 },
756
+ { formula: 'MgSO4·7H2O', amount: 394.0 },
757
+ { formula: 'Ca(H2PO4)2·H2O', amount: 67.0 },
758
+ { formula: 'Fe-EDTA·3H2O', amount: 20.0 },
759
+ { formula: 'H3BO3', amount: 1.2 },
760
+ { formula: 'MnCl2·4H2O', amount: 0.72 },
761
+ { formula: 'ZnSO4·7H2O', amount: 0.09 },
762
+ { formula: 'CuSO4·5H2O', amount: 0.04 },
763
+ { formula: 'Na2MoO4·2H2O', amount: 0.01 }
764
+ ];
765
+
766
+ selectedFertilizers.push(...preset1);
767
+ updateFertilizerList();
768
+ calculateNutrients();
769
+ }
 
770
 
771
+ function loadPreset2() {
772
+ selectedFertilizers.length = 0;
773
+
774
+ const preset2 = [
775
+ { formula: 'Ca(NO3)2·4H2O', amount: 950.0 },
776
+ { formula: 'KNO3', amount: 800.0 },
777
+ { formula: 'NH4H2PO4', amount: 150.0 },
778
+ { formula: 'MgSO4·7H2O', amount: 500.0 },
779
+ { formula: 'Fe-EDTA·3H2O', amount: 16.0 },
780
+ { formula: 'H3BO3', amount: 1.2 },
781
+ { formula: 'MnCl2·4H2O', amount: 0.72 },
782
+ { formula: 'ZnSO4·7H2O', amount: 0.09 },
783
+ { formula: 'CuSO4·5H2O', amount: 0.04 },
784
+ { formula: '(NH4)2MoO4', amount: 0.01 }
785
+ ];
786
+
787
+ selectedFertilizers.push(...preset2);
788
+ updateFertilizerList();
789
+ calculateNutrients();
790
+ }
 
 
791
 
792
+ function loadPreset3() {
793
+ selectedFertilizers.length = 0;
794
+
795
+ const preset3 = [
796
+ { formula: 'KNO3', amount: 505.0 },
797
+ { formula: 'Ca(NO3)2·4H2O', amount: 1180.0 },
798
+ { formula: 'MgSO4·7H2O', amount: 493.0 },
799
+ { formula: 'KH2PO4', amount: 136.0 },
800
+ { formula: 'H3BO3', amount: 2.86 },
801
+ { formula: 'MnCl2·4H2O', amount: 1.81 },
802
+ { formula: 'ZnSO4·7H2O', amount: 0.22 },
803
+ { formula: 'CuSO4·5H2O', amount: 0.08 },
804
+ { formula: 'Na2MoO4·2H2O', amount: 0.12 },
805
+ { formula: 'C12H12Fe2O18', amount: 5.0 }
806
+ ];
807
+
808
+ selectedFertilizers.push(...preset3);
809
+ updateFertilizerList();
810
+ calculateNutrients();
811
+ }
812
+
813
+ function clearFormula() {
814
+ selectedFertilizers.length = 0;
815
+ updateFertilizerList();
816
+ calculateNutrients();
817
+ }
818
+
819
+ // 精度處理工具函數
820
+ function roundNumber(value, precision = 3) {
821
+ if (typeof value !== 'number' || isNaN(value)) return 0;
822
+ const multiplier = Math.pow(10, precision);
823
+ // 使用四捨六入五成雙規則
824
+ const roundedValue = Math.round(value * multiplier) / multiplier;
825
+ return roundedValue;
826
+ }
827
+
828
+ // 格式化顯示數值
829
+ function formatNumber(value, precision = 3) {
830
+ const roundedValue = roundNumber(value, precision);
831
+ // 確保顯示指定的小數位數
832
+ return roundedValue.toFixed(precision);
833
+ }
834
+
835
+ function exportFormula() {
836
+ const exportContainer = document.getElementById('export-container');
837
+ const exportTextarea = document.getElementById('export-textarea');
838
+ const formulaJson = JSON.stringify(selectedFertilizers, null, 2);
839
+ exportTextarea.value = formulaJson;
840
+ exportContainer.style.display = 'block';
841
+
842
+ // Add event listener to hide the export container when clicking outside
843
+ document.addEventListener('click', handleClickOutside, true);
844
+ }
845
+
846
+ function copyToClipboard() {
847
+ const exportTextarea = document.getElementById('export-textarea');
848
+ exportTextarea.select();
849
+ document.execCommand('copy');
850
+ alert('配方已複製到剪貼簿');
851
+ }
852
+
853
+ function handleClickOutside(event) {
854
+ const exportContainer = document.getElementById('export-container');
855
+ if (!exportContainer.contains(event.target)) {
856
+ exportContainer.style.display = 'none';
857
+ document.removeEventListener('click', handleClickOutside, true);
858
+ }
859
+ }
860
+ </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
  </body>
862
  </html>