文件

220 行
11 KiB
JavaScript

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

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)});
}
}
}
};
});