feat(channel-monitor): apply template via subset picker; CC 2.1.114 baseline doc

Apply flow:
- POST /admin/channel-monitor-templates/:id/apply now requires monitor_ids
  (non-empty array). Service applies the template only to the selected
  subset, gated by AND template_id = :id (so users can't sneak in
  unrelated monitor IDs).
- New GET /admin/channel-monitor-templates/:id/monitors returns the
  associated monitor briefs (id/name/provider/enabled) for the picker.
- ApplyToMonitors signature gains monitorIDs []int64; empty list returns
  ErrChannelMonitorTemplateApplyEmpty.

Frontend:
- New MonitorTemplateApplyPickerDialog.vue: list of associated monitors
  with checkboxes (default all checked), 全选 / 全不选 shortcuts, live
  selected/total count. Submit calls apply(id, ids).
- MonitorTemplateManagerDialog replaces the old ConfirmDialog flow with
  the picker; onApplied refetches the list to refresh associated counts.

i18n: applyPicker* + common.selectAll keys.

chore: bump version to 0.1.114.33

The CC 2.1.114 (sdk-cli) UA / APIKeyBetaHeader / JSON metadata.user_id
baseline (already verified working via the in-process apply on prod
template id=1) is documented in internal/pkg/claude/constants.go and
is what the seed template in the manager UI should follow.
This commit is contained in:
erio
2026-04-21 14:39:19 +08:00
parent a296425994
commit 6925ac25c4
10 changed files with 341 additions and 51 deletions
@@ -180,14 +180,12 @@
</template>
</BaseDialog>
<ConfirmDialog
:show="confirmApply_.show"
:title="t('admin.channelMonitor.template.applyTitle')"
:message="confirmApplyMessage"
:confirm-text="t('admin.channelMonitor.template.applyConfirm')"
:cancel-text="t('common.cancel')"
@confirm="doApply"
@cancel="confirmApply_.show = false"
<MonitorTemplateApplyPickerDialog
:show="applyPicker.show"
:template-id="applyPicker.tpl ? applyPicker.tpl.id : null"
:template-name="applyPicker.tpl ? applyPicker.tpl.name : ''"
@close="applyPicker.show = false"
@applied="onApplied"
/>
<ConfirmDialog
@@ -217,6 +215,7 @@ import BaseDialog from '@/components/common/BaseDialog.vue'
import ConfirmDialog from '@/components/common/ConfirmDialog.vue'
import Icon from '@/components/icons/Icon.vue'
import MonitorAdvancedRequestConfig from '@/components/admin/monitor/MonitorAdvancedRequestConfig.vue'
import MonitorTemplateApplyPickerDialog from '@/components/admin/monitor/MonitorTemplateApplyPickerDialog.vue'
import { useChannelMonitorFormat } from '@/composables/useChannelMonitorFormat'
import {
PROVIDER_ANTHROPIC,
@@ -373,38 +372,21 @@ async function handleSubmit() {
}
}
// --- apply to monitors ---
const confirmApply_ = reactive<{ show: boolean; tpl: ChannelMonitorTemplate | null }>({
// --- apply to monitors (picker 流程) ---
const applyPicker = reactive<{ show: boolean; tpl: ChannelMonitorTemplate | null }>({
show: false,
tpl: null,
})
function confirmApply(tpl: ChannelMonitorTemplate) {
confirmApply_.tpl = tpl
confirmApply_.show = true
applyPicker.tpl = tpl
applyPicker.show = true
}
const confirmApplyMessage = computed(() => {
const tpl = confirmApply_.tpl
if (!tpl) return ''
return t('admin.channelMonitor.template.applyConfirmMessage', {
name: tpl.name,
n: tpl.associated_monitors,
})
})
async function doApply() {
const tpl = confirmApply_.tpl
confirmApply_.show = false
if (!tpl) return
try {
const { affected } = await adminAPI.channelMonitorTemplate.apply(tpl.id)
appStore.showSuccess(t('admin.channelMonitor.template.applySuccess', { n: affected }))
await fetchTemplates()
emit('updated')
} catch (err: unknown) {
appStore.showError(extractApiErrorMessage(err, t('common.error')))
}
// picker 提交后触发:刷新模板列表(拿最新 associated_monitors+ 通知父组件
async function onApplied(_affected: number) {
await fetchTemplates()
emit('updated')
}
// --- delete ---