220 行
11 KiB
JavaScript
220 行
11 KiB
JavaScript
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)});
|
||
}
|
||
}
|
||
}
|
||
};
|
||
});
|