themblem/web/src/router/index.js
Fam Zheng 2c74d59d37 Add admin configuration for verification model per batch
- Add verification_model field to CodeBatch model (TextField)
- Add verification_model field to ScanData model to track model used
- Update QrVerifyView to pass model parameter to v5 QR verify endpoint
- Create VerificationModel table to track available models
- Add VerificationModelResource API endpoint (admin only)
- Add verification-model Vue component with GenericManager
- Add verification-model route and sidebar navigation entry
- Add migrations for all database changes
- Add analysis document for feature design
2025-11-25 22:32:02 +00:00

371 lines
11 KiB
JavaScript

import { h, resolveComponent } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { authenticated } from '../auth'
import DefaultLayout from '@/layouts/DefaultLayout'
const routes = [
{
path: '/',
name: 'Home',
component: DefaultLayout,
redirect: '/dashboard',
children: [
{
path: '/tenants',
name: 'Tenants',
component: () =>
import(/* webpackChunkName: "tenants" */ '@/views/tenants.vue'),
},
{
path: '/inbox',
name: 'Inbox',
component: () =>
import(/* webpackChunkName: "inbox" */ '@/views/inbox.vue'),
},
{
path: '/products',
name: 'Products',
component: () =>
import(/* webpackChunkName: "products" */ '@/views/products.vue'),
},
{
path: '/product-properties/:id',
name: 'ProductProperties',
props: true,
component: () =>
import(/* webpackChunkName: "produc-properties" */ '@/views/product-properties.vue'),
},
{
path: '/gen-code/',
props: route => ({ batch_id: route.query.batch_id, product_id: route.query.product_id}),
name: 'GenCode',
component: () =>
import(/* webpackChunkName: "gen-code" */ '@/views/gen-code.vue'),
},
{
path: '/scan-data',
name: 'ScanData',
component: () =>
import(/* webpackChunkName: "scan-data" */ '@/views/scan-data.vue'),
},
{
path: '/scan-data/:id',
name: 'ScanDataDetails',
props: true,
component: () =>
import(/* webpackChunkName: "scan-data-details" */ '@/views/scan-data-details.vue'),
},
{
path: '/labeling',
name: 'Labeling',
component: () =>
import(/* webpackChunkName: "labeling" */ '@/views/labeling.vue'),
},
{
path: '/label-mgmt',
name: 'LabelMgmt',
component: () =>
import(/* webpackChunkName: "label-mgmt" */ '@/views/label-mgmt.vue'),
},
{
path: '/scan-data-export',
name: 'ScanDataExport',
component: () =>
import(/* webpackChunkName: "scan-data-export" */ '@/views/scan-data-export.vue'),
},
{
path: '/articles',
name: 'Articles',
component: () =>
import(/* webpackChunkName: "articles" */ '@/views/articles.vue'),
},
{
path: '/article/edit/:id',
name: 'ArticleEdit',
props: true,
component: () =>
import(/* webpackChunkName: "article-edit" */ '@/views/article-editor.vue'),
},
{
path: '/code-batch',
name: 'CodeBatch',
component: () =>
import(/* webpackChunkName: "code-batch" */ '@/views/code-batch.vue'),
},
{
path: '/verification-model',
name: 'VerificationModel',
component: () =>
import(/* webpackChunkName: "verification-model" */ '@/views/verification-model.vue'),
},
{
path: '/code-batch-export/:id',
props: true,
name: 'CodeBatchExport',
component: () =>
import(/* webpackChunkName: "code-batch-export" */ '@/views/code-batch-export.vue'),
},
{
path: '/code-batch-import/:id',
props: true,
name: 'CodeBatchImport',
component: () =>
import(/* webpackChunkName: "code-batch-import" */ '@/views/code-batch-import.vue'),
},
{
path: '/system-settings',
name: 'SystemSettings',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "system-swttings" */ '@/views/system-settings.vue'),
},
{
path: '/mini-programs',
name: 'MiniPrograms',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "mini-program" */ '@/views/mini-programs.vue'),
},
{
path: '/mini-program-content',
name: 'MiniProgramContent',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "mini-program-content" */ '@/views/mini-program-content.vue'),
},
{
path: '/camera-rule',
name: 'CameraRule',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "camera-rules" */ '@/views/camera-rule.vue'),
},
{
path: '/assets',
name: 'Assets',
props: route => ({ usage: route.query.usage }),
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "assets" */ '@/views/assets.vue'),
},
{
path: '/system-log',
name: 'SystemLog',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "system-log" */ '@/views/log.vue'),
},
{
path: '/stats',
name: 'Stats',
component: () =>
import(/* webpackChunkName: "stats" */ '@/views/stats.vue'),
},
{
path: '/code',
name: 'Code',
component: () =>
import(/* webpackChunkName: "code" */ '@/views/code.vue'),
},
{
path: '/code-feature',
name: 'CodeFeature',
component: () =>
import(/* webpackChunkName: "code-feature" */ '@/views/code-feature.vue'),
},
{
path: '/code-batch-op',
name: 'CodeBatchOp',
component: () =>
import(/* webpackChunkName: "code-batch-op" */ '@/views/code-batch-op.vue'),
},
{
path: '/code-batch-op-record',
name: 'CodeBatchOpRecord',
component: () =>
import(/* webpackChunkName: "code-batch-op-record" */ '@/views/code-batch-op-record.vue'),
},
{
path: '/dashboard',
name: 'Dashboard',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue'),
},
{
path: '/settings',
name: 'Settings',
component: () =>
import(/* webpackChunkName: "settings" */ '@/views/settings.vue'),
},
{
path: '/estor',
name: 'estor',
component: () =>
import(/* webpackChunkName: "estor" */ '@/views/estor.vue'),
},
{
path: '/estor/jobs',
name: 'estor-jobs',
component: () =>
import(/* webpackChunkName: "estor-jobs" */ '@/views/estor-jobs.vue'),
},
{
path: '/estor/disks',
name: 'estor-disks',
component: () =>
import(/* webpackChunkName: "estor-disks" */ '@/views/estor-disks.vue'),
},
{
path: '/estor/config',
name: 'estor-config',
component: () =>
import(/* webpackChunkName: "estor-config" */ '@/views/estor-config.vue'),
},
{
path: '/estor/backup-restore',
name: 'estor-backup-restore',
component: () =>
import(/* webpackChunkName: "estor-backup-restore" */ '@/views/estor-backup-restore.vue'),
},
{
path: '/estor/files',
name: 'estor-files',
component: () =>
import(/* webpackChunkName: "estor-files" */ '@/views/estor-files.vue'),
},
{
path: '/estor/import',
name: 'import',
component: () =>
import(/* webpackChunkName: "import" */ '@/views/import.vue'),
},
{
path: '/feature-upload',
name: 'feature-upload',
component: () =>
import(/* webpackChunkName: "feature-upload" */ '@/views/feature-upload.vue'),
},
{
path: '/estor/archive',
name: 'estor-archive',
component: () =>
import(/* webpackChunkName: "estor-archive" */ '@/views/archive.vue'),
},
{
path: '/estor/batches',
name: 'estor-batches',
component: () =>
import(/* webpackChunkName: "estor-batches" */ '@/views/estor-batches.vue'),
},
{
path: '/estor/search',
name: 'estor-search',
component: () =>
import(/* webpackChunkName: "estor-search" */ '@/views/estor-search.vue'),
},
{
path: '/frames',
name: 'Frames',
component: () =>
import(/* webpackChunkName: "frames" */ '@/views/frames.vue'),
},
],
},
{
path: '/pages',
redirect: '/pages/404',
name: 'Pages',
component: {
render() {
return h(resolveComponent('router-view'))
},
},
children: [
{
path: '404',
name: 'Page404',
component: () => import('@/views/pages/Page404'),
},
{
path: '500',
name: 'Page500',
component: () => import('@/views/pages/Page500'),
},
{
path: 'login',
name: 'Login',
component: () => import('@/views/pages/Login'),
},
{
path: 'forgot-password',
name: 'ForgotPassword',
component: () => import('@/views/pages/ForgotPassword'),
},
{
path: 'register',
name: 'Register',
component: () => import('@/views/pages/Register'),
},
{
path: 'article/:id',
name: 'ArticlesPreview',
props: true,
component: () => import('@/views/pages/article-preview'),
},
{
path: '/camera',
name: 'CameraView',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "camera-view" */ '@/views/camera-view.vue'),
},
],
},
{
path: '/ai-chat',
name: 'AIChat',
component: () =>
import(/* webpackChunkName: "ai-chat" */ '@/views/ai-chat.vue'),
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
scrollBehavior() {
// always scroll to top
return { top: 0 }
},
})
router.beforeEach((to, from, next) => {
if (authenticated()) {
next();
} else if (to.name !== 'Login'
&& to.name !== "ForgotPassword") {
next({ name: 'Login' })
} else {
next()
}
})
export default router