define([], function () { 'use strict'; function today() { var d = new Date(); return [d.getFullYear(), String(d.getMonth() + 1).padStart(2, '0'), String(d.getDate()).padStart(2, '0')].join('-'); } function number(v) { return v === null || v === undefined || v === '' ? '—' : String(Number(v)); } function signed(v) { return v === null ? '—' : (v > 0 ? '+' : '') + Number(v); } function requestKey() { var bytes = new Uint8Array(16); window.crypto.getRandomValues(bytes); return Array.from(bytes, function (n) { return n.toString(16).padStart(2, '0'); }).join(''); } var dateOptions = { disabledDate: function (date) { return date.getTime() > Date.now() || date.getFullYear() < 2000; } }; /** * 中储粮《粮油储藏技术规范》GB/T 29890 标准工况模板 */ var PRESETS = { quasi_low: { key: 'quasi_low', name: '准低温储粮推荐工况', tag: '准低温 (≤20℃)', type: 'primary', desc: '仓温≤20℃,绿色控温优选温区,延缓陈化', tm_i: 18.5, hm_i: 70.0, tm_o: 32.0, hm_o: 55.0 }, low: { key: 'low', name: '低温储粮标准工况', tag: '低温 (≤15℃)', type: 'success', desc: '仓温≤15℃,抑虫抑菌保鲜,高标准储粮', tm_i: 14.0, hm_i: 65.0, tm_o: 28.0, hm_o: 60.0 }, normal: { key: 'normal', name: '常温储粮常规工况', tag: '常温 (≤25℃)', type: 'info', desc: '仓温≤25℃,常规安全储粮受控温区', tm_i: 22.0, hm_i: 72.0, tm_o: 32.0, hm_o: 55.0 } }; /** * 依据中储粮温控标准研判仓内温湿度合规状态 */ function evaluateTemp(tm_i, hm_i) { if (tm_i === '' || tm_i === null || tm_i === undefined || !Number.isFinite(Number(tm_i))) { return { grade: 'unknown', label: '待设置', tagType: 'info', isHigh: false, hint: '请设置仓内温度' }; } var t = Number(tm_i); if (t <= 15.0) { return { grade: 'low', label: '低温储粮', tagType: 'success', isHigh: false, hint: '达到低温储粮标准 (≤15℃,抑虫控酶,保质保鲜最优)' }; } if (t <= 20.0) { return { grade: 'quasi_low', label: '准低温储粮', tagType: 'primary', isHigh: false, hint: '达到中储粮准低温推荐标准 (≤20℃,绿色控温优选温区)' }; } if (t <= 25.0) { return { grade: 'normal', label: '常温储粮', tagType: 'info', isHigh: false, hint: '处于常温储粮安全控温范围 (≤25℃)' }; } return { grade: 'high_warn', label: '高温超标预警', tagType: 'danger', isHigh: true, hint: '⚠️ 超过25℃中储粮控温安全线(当前' + t + '℃),高温易致发热霉变与虫害,请及时开启空调或通风降温!' }; } return { list: { template: '#forecast-list-template', props: ['rows', 'loading'], methods: { number: number, status: function (s) { return {pending: '待生成', processing: '生成中', ready: '已生成', failed: '生成失败,可重试'}[s] || s; }, evalTemp: evaluateTemp } }, create: { template: '#forecast-create-template', props: ['granaries', 'busy', 'initialData', 'isEdit'], data: function () { var d = this.initialData; return { form: { id: d && d.id ? d.id : '', gid: d && d.gid ? d.gid : '', observed_date: d && d.observed_date ? d.observed_date : today(), horizons: d && d.horizons ? d.horizons.slice() : [7, 15, 30, 60], tm_i: d && d.tm_i !== undefined ? Number(d.tm_i) : 18.5, hm_i: d && d.hm_i !== undefined ? Number(d.hm_i) : 70.0, tm_o: d && d.tm_o !== undefined ? Number(d.tm_o) : 32.0, hm_o: d && d.hm_o !== undefined ? Number(d.hm_o) : 55.0, request_key: requestKey() }, dateOptions: dateOptions, presets: PRESETS, selectedPresetKey: d ? '' : 'quasi_low', fields: [ {key: 'tm_i', label: '仓内温度', unit: '℃', min: -50, max: 80, step: 0.1, placeholder: '例:18.5'}, {key: 'hm_i', label: '仓内相对湿度', unit: '%', min: 0, max: 100, step: 0.1, placeholder: '例:70.0'}, {key: 'tm_o', label: '仓外温度', unit: '℃', min: -50, max: 80, step: 0.1, placeholder: '例:32.0'}, {key: 'hm_o', label: '仓外相对湿度', unit: '%', min: 0, max: 100, step: 0.1, placeholder: '例:55.0'} ] }; }, computed: { tempStatus: function () { return evaluateTemp(this.form.tm_i, this.form.hm_i); }, selectedGranary: function () { var gid = this.form.gid; return this.granaries.find(function (g) { return String(g.id) === String(gid); }) || null; } }, watch: { 'form.gid': function (newGid) { if (!newGid) return; if (!this.isEdit) { this.onGranaryChange(newGid); } } }, methods: { applyPreset: function (key) { var p = this.presets[key]; if (!p) return; this.selectedPresetKey = key; this.form.tm_i = p.tm_i; this.form.hm_i = p.hm_i; this.form.tm_o = p.tm_o; this.form.hm_o = p.hm_o; this.$message.success('已套用【' + p.name + '】规范温湿度'); }, onGranaryChange: function (gid) { var granary = this.granaries.find(function (g) { return String(g.id) === String(gid); }); if (!granary) return; // 若仓库自带最近一次历史环境记录 last_env,则智能推荐带出 if (granary.last_env && granary.last_env.tm_i !== undefined && granary.last_env.tm_i !== '') { this.form.tm_i = Number(granary.last_env.tm_i); this.form.hm_i = Number(granary.last_env.hm_i); this.form.tm_o = Number(granary.last_env.tm_o); this.form.hm_o = Number(granary.last_env.hm_o); this.selectedPresetKey = ''; this.$message.info('已自动读取【' + granary.name + '】最近一次温湿度记录'); } else if (this.form.tm_i === '' || this.form.tm_i === undefined) { this.applyPreset('quasi_low'); } }, submit: function () { if (!this.form.gid || !this.form.observed_date || !this.form.horizons.length) return this.$message.warning('请选择仓库、采集日期和预测周期'); for (var i = 0; i < this.fields.length; i++) { var f = this.fields[i], v = this.form[f.key]; if (v === '' || !Number.isFinite(Number(v)) || Number(v) < f.min || Number(v) > f.max) return this.$message.warning('请填写有效的' + f.label + '(' + f.min + '~' + f.max + ')'); } this.$emit('submit', JSON.parse(JSON.stringify(this.form))); } } }, detail: { template: '#forecast-detail-template', props: ['record', 'busy'], data: function () { return { active: '', actualDate: '', actual: {}, baseline: '', dateOptions: dateOptions }; }, computed: { point: function () { var id = this.active; return this.record.points.find(function (p) { return String(p.id) === id; }); }, dirty: function () { return this.baseline !== this.snapshot(); }, comparison: function () { if (!this.point) return []; var actual = this.actual; return this.point.rows.map(function (row) { var value = actual[row.key], val = value === '' || value === null || value === undefined || !Number.isFinite(Number(value)) ? null : Number(value); var delta = val !== null && row.predicted !== null ? Number((val - row.predicted).toFixed(4)) : null; return Object.assign({}, row, {delta: delta, percent: delta !== null && row.predicted !== 0 ? Number((delta / Math.abs(row.predicted) * 100).toFixed(2)) : null}); }); } }, watch: { active: function () { this.reset(); }, record: { immediate: true, handler: function () { if (!this.record.points.some(function (p) { return String(p.id) === this.active; }, this)) this.active = String(this.record.points[0].id); this.reset(); } } }, methods: { number: number, signed: signed, snapshot: function () { return JSON.stringify([this.actualDate, this.actual]); }, reset: function () { var point = this.point; this.actualDate = point.actual_date || ''; var values = {}; this.record.metrics.forEach(function (m) { values[m.key] = point.actual[m.key] == null ? '' : String(point.actual[m.key]); }); this.actual = values; this.baseline = this.snapshot(); }, confirmDiscard: function () { if (this.busy) return Promise.reject(); return this.dirty ? this.$confirm('当前周期有未保存的实测数据,确定放弃修改?', '未保存的数据', {type: 'warning'}) : Promise.resolve(); }, beforeLeave: function () { return this.confirmDiscard().then(function () { return true; }); }, save: function () { var any = false; for (var i = 0; i < this.record.metrics.length; i++) { var m = this.record.metrics[i], v = this.actual[m.key]; if (v === '') continue; any = true; if (!Number.isFinite(Number(v)) || Number(v) < 0 || Number(v) > m.max) return this.$message.warning('请填写有效的' + m.name + '实测值'); } if (any && !this.actualDate) return this.$message.warning('请选择实际检测日期'); this.$emit('save', {point_id: this.point.id, version: this.point.version, actual_date: this.actualDate, actual: Object.assign({}, this.actual)}); } } } }; });