feat(forecast): 支持温湿度预测记录编辑与删除,修复组件调用并级联清理周期数据

这个提交包含在:
wikiboss
2026-11-02 14:55:36 +08:00
父节点 4a1e4f1d93
当前提交 f6e454a589
共有 8 个文件被更改,包括 284 次插入 和 61 次删除

查看文件

@@ -33,29 +33,29 @@ class Forecast extends GranaryBackend
}
}
unset($row);
$granaries = array_values(array_filter(GranarySite::granaries(->filterSites), function () { return in_array(['varieties'], ['小麦', '稻谷'], true); }));
if (!empty()) {
= array_column(, 'id');
= Db::name('grain_forecast')->where('gid', 'in', )
$granaries = array_values(array_filter(GranarySite::granaries($this->filterSites), function ($g) { return in_array($g['varieties'], ['小麦', '稻谷'], true); }));
if (!empty($granaries)) {
$gids = array_column($granaries, 'id');
$envRecords = Db::name('grain_forecast')->where('gid', 'in', $gids)
->field('gid,tm_i,hm_i,tm_o,hm_o,observed_date')->order('id DESC')->select();
= [];
foreach ( as ) {
if (!isset([['gid']])) {
[['gid']] = [
'tm_i' => (float)['tm_i'],
'hm_i' => (float)['hm_i'],
'tm_o' => (float)['tm_o'],
'hm_o' => (float)['hm_o'],
'observed_date' => ['observed_date']
$envMap = [];
foreach ($envRecords as $rec) {
if (!isset($envMap[$rec['gid']])) {
$envMap[$rec['gid']] = [
'tm_i' => (float)$rec['tm_i'],
'hm_i' => (float)$rec['hm_i'],
'tm_o' => (float)$rec['tm_o'],
'hm_o' => (float)$rec['hm_o'],
'observed_date' => $rec['observed_date']
];
}
}
foreach ( as &) {
['last_env'] = isset([['id']]) ? [['id']] : null;
foreach ($granaries as &$g) {
$g['last_env'] = isset($envMap[$g['id']]) ? $envMap[$g['id']] : null;
}
unset();
unset($g);
}
return json(['code' => 200, 'list' => , 'total' => , 'granaries' => ]);
return json(['code' => 200, 'list' => $list, 'total' => $total, 'granaries' => $granaries]);
}
public function detail()
@@ -76,6 +76,38 @@ class Forecast extends GranaryBackend
});
}
public function edit($ids = null)
{
return $this->write(function ($service) use ($ids) {
$id = $this->request->post('id/d', $ids ?: 0);
$id = $service->update($id, $this->request->post(), $this->auth->id);
$service->generate($id);
return $id;
});
}
public function dell($ids = null)
{
if (!$this->request->isPost()) return json(['code' => 405, 'msg' => '请使用POST提交']);
$id = $this->request->post('id', $ids ?: $this->request->get('id'));
if (empty($id)) return json(['code' => 400, 'msg' => '请指定要删除的记录编号']);
try {
$service = new GrainForecast();
$service->delete($id);
return json(['code' => 200, 'msg' => '删除成功']);
} catch (\InvalidArgumentException $e) {
return json(['code' => 400, 'msg' => $e->getMessage()]);
} catch (\Exception $e) {
\think\Log::record('Grain forecast delete: ' . $e->getMessage(), 'error');
return json(['code' => 500, 'msg' => '删除失败,请刷新后重试']);
}
}
public function del($ids = null)
{
return $this->dell($ids);
}
public function retry()
{
return $this->write(function ($service) {

查看文件

@@ -27,11 +27,14 @@
<el-button type="primary" icon="el-icon-plus" size="small" @click="newRecord">录入温湿度并预测</el-button>
</div>
<p class="forecast-intro">选择仓库,录入采集当天的仓内外温湿度,生成未来品质指标预测;取得检测结果后,在对应周期补录实测值并查看偏差。</p>
<forecast-list :rows="rows" :loading="loading" @open="openRecord"></forecast-list>
<forecast-list :rows="rows" :loading="loading" @open="openRecord" @edit="editRecord" @delete="deleteRecord"></forecast-list>
<el-pagination background layout="total, prev, pager, next" :total="total" :page-size="15" :current-page="page" @current-change="changePage"></el-pagination>
<el-dialog title="录入温湿度并预测" :visible.sync="creating" width="760px" :close-on-click-modal="false" :before-close="closeCreate" :show-close="!saving">
<forecast-create v-if="creating" ref="create" :granaries="granaries" :busy="saving" @submit="createRecord" @cancel="closeCreate"></forecast-create>
</el-dialog>
<el-dialog title="编辑温湿度并更新预测" :visible.sync="editing" width="760px" :close-on-click-modal="false" :before-close="closeEdit" :show-close="!saving">
<forecast-create v-if="editing && editData" ref="edit" :granaries="granaries" :busy="saving" :initial-data="editData" :is-edit="true" @submit="updateRecord" @cancel="closeEdit"></forecast-create>
</el-dialog>
<el-dialog title="预测与真实监测值对比" :visible.sync="viewing" width="1180px" :close-on-click-modal="false" :before-close="closeDetail" :show-close="!saving">
<forecast-detail v-if="viewing && selected" ref="detail" :record="selected" :busy="saving" @save="saveActual" @retry="retry"></forecast-detail>
<div class="forecast-foot"><el-button @click="closeDetail" :disabled="saving">关闭</el-button></div>
@@ -55,7 +58,13 @@
<el-table-column label="仓外温湿度" min-width="145"><template slot-scope="s">{{number(s.row.tm_o)}}℃ / {{number(s.row.hm_o)}}%</template></el-table-column>
<el-table-column label="预测状态" width="130"><template slot-scope="s"><el-tag :type="s.row.status === 'ready' ? 'success' : s.row.status === 'failed' ? 'danger' : 'warning'" size="small">{{status(s.row.status)}}</el-tag></template></el-table-column>
<el-table-column label="实测完成周期" width="120"><template slot-scope="s">{{s.row.measured}} / {{s.row.point_count}}</template></el-table-column>
<el-table-column label="操作" width="150" fixed="right"><template slot-scope="s"><el-button type="text" @click="$emit('open', s.row.id)">查看 / 补录实测</el-button></template></el-table-column>
<el-table-column label="操作" width="220" fixed="right">
<template slot-scope="s">
<el-button type="text" size="small" @click="$emit('open', s.row.id)">查看 / 补录实测</el-button>
<el-button type="text" size="small" @click="$emit('edit', s.row)">编辑</el-button>
<el-button type="text" size="small" style="color: #F56C6C;" @click="$emit('delete', s.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</script>
@@ -97,7 +106,10 @@
</el-form-item>
</div>
<p class="forecast-hint"><strong>中储粮控温技术规范:</strong>低温储粮 ≤15℃,准低温储粮 ≤20℃,常温储粮 ≤25℃(>25℃需通风或机械降温)。仓内相对湿度建议 ≤70%~75%。预测目标日期按采集日期加周期计算。</p>
<div class="forecast-foot"><el-button :disabled="busy" @click="$emit('cancel')">取消</el-button><el-button type="primary" :loading="busy" @click="submit">保存并生成预测</el-button></div>
<div class="forecast-foot">
<el-button :disabled="busy" @click="$emit('cancel')">取消</el-button>
<el-button type="primary" :loading="busy" @click="submit">{{ isEdit ? '保存修改并重新生成预测' : '保存并生成预测' }}</el-button>
</div>
</el-form>
</script>