mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 10:00:30 +08:00
feat(m9): add subscription report config (localStorage MVP)
This commit is contained in:
@@ -136,6 +136,7 @@ const menuItems = [
|
|||||||
{ path: "/devices", icon: "🖥️", label: "设备管理", roles: ["SYS_ADMIN","SALES","DELIVERY"] },
|
{ path: "/devices", icon: "🖥️", label: "设备管理", roles: ["SYS_ADMIN","SALES","DELIVERY"] },
|
||||||
{ path: "/todos", icon: "🔔", label: "待办中心", roles: ["SYS_ADMIN","SALES","LICENSE_OPS"] },
|
{ path: "/todos", icon: "🔔", label: "待办中心", roles: ["SYS_ADMIN","SALES","LICENSE_OPS"] },
|
||||||
{ path: "/reports/contract-sn", icon: "📊", label: "报表中心", roles: ["SYS_ADMIN"] },
|
{ path: "/reports/contract-sn", icon: "📊", label: "报表中心", roles: ["SYS_ADMIN"] },
|
||||||
|
{ path: "/reports/subscriptions", icon: "📧", label: "报表订阅", roles: ["SYS_ADMIN"] },
|
||||||
];
|
];
|
||||||
|
|
||||||
const visibleMenu = computed(() => menuItems.filter(m => auth.hasAnyRole(m.roles)));
|
const visibleMenu = computed(() => menuItems.filter(m => auth.hasAnyRole(m.roles)));
|
||||||
|
|||||||
@@ -188,6 +188,12 @@ const routes = [
|
|||||||
component: () => import("../views/ProfileView.vue"),
|
component: () => import("../views/ProfileView.vue"),
|
||||||
meta: { roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"], title: "个人设置" },
|
meta: { roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"], title: "个人设置" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "reports/subscriptions",
|
||||||
|
name: "report-subscriptions",
|
||||||
|
component: () => import("../views/SubscriptionReportView.vue"),
|
||||||
|
meta: { roles: ["SYS_ADMIN"], title: "报表订阅" },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "reports/project-health",
|
path: "reports/project-health",
|
||||||
name: "project-health",
|
name: "project-health",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ const allModuleLinks = [
|
|||||||
{ to: "/devices", label: "设备管理", roles: ["SYS_ADMIN", "SALES", "DELIVERY"] },
|
{ to: "/devices", label: "设备管理", roles: ["SYS_ADMIN", "SALES", "DELIVERY"] },
|
||||||
{ to: "/todos", label: "待办中心", roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"] },
|
{ to: "/todos", label: "待办中心", roles: ["SYS_ADMIN", "SALES", "LICENSE_OPS"] },
|
||||||
{ to: "/reports/contract-sn", label: "报表中心", roles: ["SYS_ADMIN"] },
|
{ to: "/reports/contract-sn", label: "报表中心", roles: ["SYS_ADMIN"] },
|
||||||
|
{ to: "/reports/subscriptions", label: "报表订阅", roles: ["SYS_ADMIN"] },
|
||||||
];
|
];
|
||||||
|
|
||||||
const visibleModuleLinks = computed(() => allModuleLinks.filter((l) => auth.hasAnyRole(l.roles)));
|
const visibleModuleLinks = computed(() => allModuleLinks.filter((l) => auth.hasAnyRole(l.roles)));
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'craftlabs_report_subscriptions'
|
||||||
|
const subscriptions = ref([])
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const editingIndex = ref(-1)
|
||||||
|
const form = ref({ reportType: 'contract-sn', schedule: 'weekly', email: '', enabled: true })
|
||||||
|
|
||||||
|
function load() {
|
||||||
|
try {
|
||||||
|
const data = localStorage.getItem(STORAGE_KEY)
|
||||||
|
subscriptions.value = data ? JSON.parse(data) : []
|
||||||
|
} catch { subscriptions.value = [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(subscriptions.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreate() {
|
||||||
|
editingIndex.value = -1
|
||||||
|
form.value = { reportType: 'contract-sn', schedule: 'weekly', email: '', enabled: true }
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEdit(idx) {
|
||||||
|
editingIndex.value = idx
|
||||||
|
form.value = { ...subscriptions.value[idx] }
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSave() {
|
||||||
|
if (editingIndex.value >= 0) {
|
||||||
|
subscriptions.value[editingIndex.value] = { ...form.value }
|
||||||
|
} else {
|
||||||
|
subscriptions.value.push({ ...form.value, id: Date.now() })
|
||||||
|
}
|
||||||
|
save()
|
||||||
|
dialogVisible.value = false
|
||||||
|
ElMessage.success('订阅已保存')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(idx) {
|
||||||
|
subscriptions.value.splice(idx, 1)
|
||||||
|
save()
|
||||||
|
ElMessage.success('订阅已删除')
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleEnabled(idx) {
|
||||||
|
subscriptions.value[idx].enabled = !subscriptions.value[idx].enabled
|
||||||
|
save()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(load)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="toolbar">
|
||||||
|
<span class="title">报表订阅</span>
|
||||||
|
<el-button type="success" @click="openCreate">新建订阅</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-empty v-if="subscriptions.length === 0" description="暂未配置报表订阅" />
|
||||||
|
<el-table v-else :data="subscriptions" stripe>
|
||||||
|
<el-table-column label="报表类型" width="160">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.reportType === 'contract-sn' ? '履约对账' : row.reportType === 'callback-stats' ? 'Callback 统计' : row.reportType }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="schedule" label="推送频率" width="120" />
|
||||||
|
<el-table-column prop="email" label="接收邮箱" min-width="200" />
|
||||||
|
<el-table-column label="启用" width="80">
|
||||||
|
<template #default="{ row, $index }">
|
||||||
|
<el-switch :model-value="row.enabled" @change="toggleEnabled($index)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="140">
|
||||||
|
<template #default="{ $index }">
|
||||||
|
<el-button type="primary" link @click="openEdit($index)">编辑</el-button>
|
||||||
|
<el-button type="danger" link @click="handleDelete($index)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="dialogVisible" :title="editingIndex >= 0 ? '编辑订阅' : '新建订阅'" width="480px">
|
||||||
|
<el-form label-width="100px">
|
||||||
|
<el-form-item label="报表类型" required>
|
||||||
|
<el-select v-model="form.reportType" style="width:100%">
|
||||||
|
<el-option label="履约对账" value="contract-sn" />
|
||||||
|
<el-option label="Callback 统计" value="callback-stats" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="推送频率" required>
|
||||||
|
<el-select v-model="form.schedule" style="width:100%">
|
||||||
|
<el-option label="每日" value="daily" />
|
||||||
|
<el-option label="每周" value="weekly" />
|
||||||
|
<el-option label="每月" value="monthly" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="接收邮箱" required>
|
||||||
|
<el-input v-model="form.email" placeholder="email@example.com" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toolbar { display: flex; justify-content: space-between; align-items: center; }
|
||||||
|
.title { font-weight: 600; font-size: 16px; }
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user