npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mvframe-nuxt

v0.0.39

Published

Nuxt full-stack framework layer inspired by mvframe.

Readme

mvframe-nuxt

Nuxt full-stack framework package inspired by mvframe.

This package is intentionally separate from the existing mvframe Vue/Vite library. It keeps mvframe stable while providing a Nuxt module path for full-stack app work: auto-registered UI components, composables, runtime config, and Nitro server handlers.

Install

yarn add mvframe-nuxt
export default defineNuxtConfig({
  modules: ["mvframe-nuxt"],
  mvframe: {
    config: {
      name: "My App",
      lang: "en_us",
      mode: "dark",
      theme: "glass",
      baseURL: "https://api.example.com",
      elementPlus: {
        size: "default",
        zIndex: 3000,
      },
    },
    server: {
      enabled: true,
      prefix: "/api/mvframe",
      proxy: {
        enabled: false,
        target: "",
        headers: {},
      },
    },
  },
});

Host AI Rules

Initialize the package's Cursor and Codex rules in the host project:

yarn exec mvframe-nuxt-init-rules --force

The command copies the current installed package README to .mvframe-nuxt/docs/README.md and adds a relative link to it in the generated AGENTS.md section. Codex can then read the same component usage that is maintained in this README. Run the command again after upgrading mvframe-nuxt so the host documentation and component list match the installed version.

DingTalk Completion Notifications

Initialize the host-only notification environment file from the project root:

yarn exec mvframe-nuxt-init-notify

The command uses the package's test DingTalk robot by default. A host project can override it with --webhook and --secret, or with the DINGTALK_WEBHOOK and DINGTALK_SECRET process environment variables.

For automation where secrets should not appear in process arguments, pass a JSON object through standard input:

printf '%s' '{"webhook":"https://oapi.dingtalk.com/robot/send?access_token=...","secret":"SEC..."}' \
  | yarn exec mvframe-nuxt-init-notify --stdin

The command updates DINGTALK_WEBHOOK and DINGTALK_SECRET in .env.mvframe-notify, preserves unrelated settings, applies file mode 0600, and never prints the configured values. Override the target with --env <path> when needed. Keep .env.mvframe-notify out of version control.

Declare the notification source through the module's private server interface:

export default defineNuxtConfig({
  mvframe: {
    server: {
      notify: {
        enabled: true,
        envFile: ".env.mvframe-notify",
      },
    },
  },
});

mvframe.server.notify also accepts webhook, secret, atMobiles, atUserIds, atAll, and timeout. The module resolves explicit values first, then process environment variables, then envFile. The resolved object is stored only in private runtimeConfig.mvframe.server.notify; it is never returned by /api/mvframe/config or exposed through runtimeConfig.public.

Send one completion message without starting the local notification server:

yarn exec mvframe-nuxt-notify --once --message "Title
Summary"

Runtime Surface

  • Components: MvFrame, MvPage, MvCard, MvTable, MvVTable, MvEChart, MvDatePicker, MvSingleDatePicker, MvSelect, MvSelectV2, MvDropdown, MvBtnGroup, MvMode, MvTheme, MvPrimaryColor, MvList, MvNote, MvLoading, MvIcon
  • Composables: useMvConfig, useMvBootstrap, useMvRq, useMvGet, useMvPost, useMvNotify
  • Server routes: /api/mvframe/health, /api/mvframe/config, /api/mvframe/proxy/**
  • Theme defaults: mode: "dark" and theme: "glass"; the client plugin applies html.dark.mv-theme-glass.
  • MvBtnGroup has no built-in group background; pass bg="white" or another global bg-* suffix when a filled surface is needed. MvTabs forwards bg to its internal MvBtnGroup.

Request Language

useMvRq appends the current plugin language as a lang query field on every request. It reads the value through the same runtime language getter used by the rest of the plugin, and preserves an explicit query.lang when the caller provides one.

Remote Language Maps

mvframe.config.langApi can load host-provided language maps before $l is mounted. The endpoint receives the current language through queryKey and may return {data: {maps}}, {maps}, or a raw key-value map. Remote maps are merged after static langMaps, so database values can override local defaults.

export default defineNuxtConfig({
  mvframe: {
    config: {
      lang: "en_us",
      langApi: {
        enabled: true,
        endpoint: "/api/mvframe/language",
        queryKey: "lang",
        timeout: 5000,
      },
    },
  },
});

Bootstrap Before Route Render

Use useMvBootstrap().setHandler() for reusable post-login authorization or dictionary loading that must finish before normal pages render. The module installs a global route middleware; if no handler or endpoint is configured, it immediately lets the route continue.

Register the async handler in a host plugin. Do not put the function in runtimeConfig.public, because Nuxt serializes runtime config.

export default defineNuxtPlugin(() => {
  const bootstrap = useMvBootstrap();

  bootstrap.setHandler(async ({ $fetch }) => {
    if (!$fetch) return;

    const [auth, dictionaries] = await Promise.all([
      $fetch("/api/auth/bootstrap"),
      $fetch("/api/mvframe/dictionaries"),
    ]);

    return [auth, dictionaries];
  });
});

Use mvframe.config.bootstrap only for route matching and error behavior:

export default defineNuxtConfig({
  mvframe: {
    config: {
      bootstrap: {
        excludePaths: ["/login"],
        redirectOnError: "/login",
        timeout: 10000,
      },
    },
  },
});

The handler may return nothing when it only needs to wait. If it returns an object, or an array of objects, config, langMaps, and maps are merged into mvframe runtime state. Values may be returned at the response root or under data:

export default defineEventHandler(() => ({
  data: {
    config: { accountId: "123" },
    langMaps: { Search: "搜索" },
    maps: { asa: { matchType: { obj: {} } } },
  },
}));

For a single simple request, bootstrap.endpoint is still supported and is used when no handler is registered. After a login action, call useMvBootstrap().refresh() to rerun the check with the new auth state.

Map Language Cache

useMap.lang(path) translates map labels lazily and caches the translated result per path. The cache is cleared when bootstrap patches langMaps / maps, or when runtime maps are applied again. Timezone maps (common.timezone, common.timezoneAll) are excluded from label translation by default.

Data Helpers

Data normalization helpers are exported from mvframe-nuxt/composition. Prefer the direct helper when the target type is known; use typedValue only for dynamic type dispatch.

import {stringValue, numberValue, typedValue} from "mvframe-nuxt/composition";

const keyword = stringValue(route.query.keyword, {trim: true});
const pageSize = numberValue(route.query.pageSize, {integer: true, min: 1, fallback: 20});
const value = typedValue(input, {type: "string", trim: true});

MvInput

MvInput forwards Element Plus input props, including type="textarea". It supports fixed height through height, with number values and numeric strings treated as pixels and CSS size strings used as-is.

Field component labels are translated inside MvInput, MvInputNumber, MvTextarea, MvMention, MvSelect, and MvSelectV2. Pass the original language key or text to label; callers should not wrap it with $l() first.

The same rule applies to title and label props handled by other mvframe-nuxt components. MvForm also translates descendant el-form-item labels through its form-item middleware. Plain DOM copy, business options, buttons, tooltips, validation, and message text remain the caller's responsibility unless their component explicitly handles them.

<MvInput v-model="name" type="textarea" label="Campaign Name" material-label height="120" />

MvInput type="textarea" blocks Enter from inserting a hard line break. IME composition is guarded, so pressing Enter to confirm a pinyin candidate does not trigger the normal enter flow.

Use nobreak when textarea content must not soft-wrap. In textarea mode, MvInput will set wrap="off" unless the caller already passed a wrap value.

<MvInput v-model="name" type="textarea" label="Campaign Name" material-label height="120" nobreak />

MvTextarea keeps normal multiline behavior by default. Add nobreak only when it should block Enter line breaks and soft wrapping:

<MvTextarea v-model="keywords" height="120" nobreak resize="none" />

MvLogin

MvLogin includes a persistent Light/Dark mode switch and built-in Login, Register, and Forget Password panels. The default Register and Forget Password forms emit register and forget; use v-model:panel when the host also needs the active panel.

<MvLogin
  v-model="loginForm"
  v-model:panel="panel"
  :submit="login"
  @register="register"
  @forget="sendReset"
/>

Use the register or forget slot to replace that panel's entire form. Slot submissions are owned by the host and do not invoke the corresponding MvLogin emit. Both slots expose form, errors, loading, showLogin(), and switchPanel(panel).

<MvLogin>
  <template #register="{showLogin}">
    <form @submit.prevent="registerDirectly">
      <!-- Host-owned fields and submit behavior. -->
      <button type="button" @click="showLogin">Back to Login</button>
    </form>
  </template>
</MvLogin>

MvDatePicker

MvDatePicker wraps Element Plus el-date-picker as a daterange field with mvframe material-label styling and built-in translation for label, placeholders, and shortcuts.

<MvDatePicker
  v-model="dateRange"
  label="Date Range"
  material-label
  shortcuts
  clearable
/>

By default it uses the last 7 days excluding today through default-date="[-7, -1]". Pass :default-date="false" when a date filter should start empty. It emits update:startDate and update:endDate in addition to v-model.

MvSingleDatePicker uses the same field style for single-day values:

<MvSingleDatePicker v-model="date" label="Current Date" material-label />

It emits a YYYY-MM-DD string. Pass :default-date="-1" to initialize yesterday, or keep the default false to start empty.

MvSelect / MvSelectV2

MvSelect and MvSelectV2 wrap Element Plus select controls with mvframe field styling. Use loading while options are being fetched; the field shows imicon im-loading ani-rotate on the left and blocks dropdown opening until loading is cleared.

MvSelect forwards its header slot to Element Plus. MvBtnGroup supports icon-only for icon filters; in that mode each translated option label is exposed through aria-label and the global tipbtn tooltip. Use tip-placement to set the tooltip direction.

In multiple mode, option changes remain pending until the built-in Confirm action is used. The clear button is immediate: it commits the cleared value and emits update:modelValue, change, and clear.

<MvSelect v-model="accountId" label="Account" material-label :options="accountOptions">
  <template #header>
    <MvBtnGroup
      v-model="accountType"
      :options="accountTypeOptions"
      icon-only
      tip-placement="bottom"
      size="small"
    />
  </template>
</MvSelect>

MvDropdown

MvDropdown wraps Element Plus dropdown with a button trigger and option mapping. The trigger shows its label and the selected count in a primary tag, while selected menu items use is-active. It supports single or multiple values, optional menu filtering, and FIFO selection limits. When a multiple selection exceeds multiple-limit, the earliest selected value is removed automatically.

<MvDropdown
  v-model="metrics"
  :options="metricOptions"
  label="Metrics"
  multiple
  filterable
  :multiple-limit="5"
/>

Element Plus Config

Element Plus is installed by the module. Put global Element Plus config under mvframe.config.elementPlus; it is passed to app.use(ElementPlus, config).

export default defineNuxtConfig({
  mvframe: {
    config: {
      elementPlus: {
        size: "large",
        zIndex: 3000,
        namespace: "el",
      },
    },
  },
});

idPrefix is also supported as an mvframe-nuxt SSR id provider option and is not passed to Element Plus.

Frame Menu

For a menu group with child routes, set default: true on the child that should open when the parent is clicked. The default route may also be hidden; it remains available as the parent's navigation target while staying out of the rendered submenu. If no enabled default route exists, MvFrame keeps the existing behavior of opening the first enabled visible child.

{
  label: "Campaign",
  children: [
    {path: "/campaigns", default: true, hidden: true},
    {path: "/createcampaigns", hidden: true},
  ],
}

Frame Tabs

Frame tabs are recorded by the built-in router guard when mvframe.config.pinia.useTab is enabled. A page can opt out with route meta:

definePageMeta({
  title: "No Account",
  noTab: true,
});

Equivalent supported flags are tab: false and saveTab: false. To remove already-saved tabs or exclude generated routes by rule, configure names or paths:

export default defineNuxtConfig({
  mvframe: {
    config: {
      tab: {
        excludeNames: ["Common_NoAccount"],
        excludePaths: ["/common/no-account"],
      },
    },
  },
});

Frame Tools

MvFrame renders its built-in tools, including the Language switcher and Setting tool, on the right side of the tools area. The language switcher reuses MvLang, so changes follow the same localStorage / cookie / reload flow. Use #tools to insert host-specific entries before those built-in tools; the built-in tools remain rendered after the slot. By default, the Setting tool opens a built-in Appearance drawer with MvMode, MvTheme, and a persistent primary color control.

<MvFrame>
  <template #tools>
    <ModuleSwitcher />
    <UserPopover />
  </template>
</MvFrame>

Use the setting slot when the host only needs to replace the contents while retaining the built-in drawer:

<MvFrame>
  <template #setting="{close}">
    <MySettingPanel @close="close" />
  </template>
</MvFrame>

tool-action receives the tool and a synchronous action context. Call preventDefault() when the host owns the entire Setting workflow, such as opening an application-level drawer. Listeners that only observe or report tool actions do not disable the built-in fallback.

<MvFrame @tool-action="onToolAction" />

<script setup lang="ts">
import type {
  MvFrameToolActionContext,
  MvFrameToolItem,
} from "mvframe-nuxt/types";

function onToolAction(tool: MvFrameToolItem, context: MvFrameToolActionContext) {
  if (!tool.__mvframeSetting) return;
  context.preventDefault();
  openApplicationSettingDrawer();
}
</script>

Set :show-lang-tool="false" to hide the built-in language switcher. Set :show-setting-tool="false" to hide the Setting tool.

MvMode, MvTheme, and MvPrimaryColor can also be used independently. They read the applied frame theme when model-value is omitted, emit update:modelValue and change, and persist changes by default.

<MvMode v-model="mode" />
<MvTheme v-model="theme" />
<MvPrimaryColor v-model="primary" />

MvPrimaryColor includes a color picker, HEX input, preset colors, validation, and a restore-default action. Pass :predefine="brandColors" to replace its presets, or :persist="false" when the host owns persistence. Restoring the default emits null through v-model; with persistence enabled, it also removes the persisted primary override.

MvDialog

MvDialog locks page body scrolling by default while it is open. Long dialog content should scroll inside MvDialogArea / .MvcDialogBody; do not let the page body become the scroll container behind the modal.

<MvDialog v-model:current="current" :dialog="dialogMap" />
<MvDialogArea height="560">
  <LargeDialogContent />
</MvDialogArea>

MvDialogArea wraps its default slot in an internal el-scrollbar. Use height or max-height to define the body scroll area. For large or frequently changing dialog bodies, pass native to use the browser scrollbar and avoid custom-thumb rendering lag. Only use noscroll for fixed, already-contained content. A dialog item can opt out of body locking with lockScroll: false, but the default should stay locked for modal workflows.

Host App Structure

mvframe-nuxt-init-app generates Nuxt-native host paths instead of the old Vue/Vite-style src/* aggregate tree:

  • components/AdminEntry.vue for the application shell around MvFrame
  • config/frame-routes.ts for Frame menu/tab metadata; Nuxt routing still comes from pages/
  • config/mvframe.ts for build-time mvframe.config defaults consumed by nuxt.config.ts
  • components/<module> for page-level business components
  • composables/ for host request/composition helpers
  • server/api/ for host API routes
  • maps/index.ts only when the host has real business maps to pass through mvframe.config.maps

Do not scaffold placeholder src/api, src/pinia/chip, src/router, src/component, src/composition, or src/assets/style directories in Nuxt host apps.

MvVTable Tools

MvVTable supports the same built-in table tools as MvTable: refresh, column customization, and CSV download.

<MvVTable
  table-name="campaigns"
  :columns="columns"
  :records="rows"
  :tool="{ column: true, download: true }"
/>

Use tool=true to enable all tools. Use tool="{ refresh: false, column: true, download: true }" to hide refresh. Column settings are stored by tableName. Columns with visible: false are hidden only by default and can still be enabled from Columns. Download opens a column picker, defaults to the current visible columns, and can export only selected rows when a selection column is active.

MvVTable Border and Radius

Use border to configure the component frame and its header/footer separators. It accepts true for the default border, false to remove those borders, or a complete CSS border value. Use border-radius with a number (treated as pixels by the shared size converter) or a CSS size. The internal VTable cell grid is not affected by the frame border prop.

<MvVTable
  :columns="columns"
  :records="rows"
  border="2px solid var(--color-primary)"
  :border-radius="12"
/>

<MvVTable
  :columns="columns"
  :records="rows"
  :border="false"
  :border-radius="0"
/>

MvVTable Cell Tooltip

Set tooltip: true on a column to show the full cell text when the rendered text overflows. Pass a string to always show fixed tooltip content, or pass an object for placement, style, delay, and row-aware content. String content and formatter results are translated inside MvVTable through globalThis.$l; consumers should pass the original language key or text.

const columns = [
  { field: "campaignName", title: "Campaign Name", minWidth: 240, tooltip: true },
  { field: "url", title: "URL", tooltip: "Open Product Page" },
  {
    field: "status",
    title: "Status",
    tooltip: {
      content: (record) => record.enabled ? "Enabled" : "Disabled",
      placement: "top",
    },
  },
];

MvVTable Custom Layout Interaction

Elements returned by a customRenderLayout column can expose page-owned actions through customLayoutAction. An action with type: "copy" uses the component clipboard fallback and feedback message; @custom-layout-click is also emitted for page-owned actions.

const meta = {
  mvCustomLayout: true,
  customLayoutKey: "app:123",
  customLayoutGroup: "app:123",
  customLayoutRole: "copy",
  customLayoutAction: {type: "copy", text: "Example App (123)"},
};

const elements = [
  {
    type: "image",
    src: icon,
    x: 0,
    y: 0,
    width: 28,
    height: 28,
    cursor: "pointer",
    pickable: true,
    ...meta,
  },
];

MvVTable Double-Click Copy

Use copy-on-dblclick to copy normal cell text on double click. Selection, index, operation, expand, and columns without a field are excluded by default.

<MvVTable
  :columns="columns"
  :records="rows"
  copy-on-dblclick
  @cell-copy="handleCellCopy"
/>

Column-level copyable has the highest priority. Use it to enable a single column without a table default, disable a special column, or copy a formatted field.

const columns = [
  { field: "campaignName", title: "Campaign Name", copyable: true },
  { field: "budget", title: "Budget", copyable: { field: "budgetFormat" } },
  {
    field: "__actions",
    title: "Actions",
    type: "operation",
    copyable: false,
  },
];

copy-on-dblclick also accepts an object: { included, excluded, message, successText, errorText }. Use message: false when the page wants to own feedback. @cell-copy-error is emitted when clipboard writing fails.

To avoid conflicts with copy and row/cell actions, MvVTable does not allow direct click or doubleclick cell-edit triggers. Cell editing should be opened through cellActions with trigger: "edit", startEditCell, or optional edit-cell-trigger="keydown".

MvVTable Mono Columns

Set mono: true on ID, code, token, or metric columns that should use a free system monospace font stack. Keep mixed-language text columns on the default UI font.

const columns = [
  { field: "spend", title: "Spend", align: "right", mono: true },
  { field: "campaignName", title: "Campaign Name", minWidth: 240 },
];

MvVTable Metric Columns

MvVTable can consume metric-like column definitions directly. A column with value and unit / precision is treated as a metric column; formatter remains the formatted field key such as spendFormat, and missing formatted values are calculated at display/download time with $fa / $fu semantics.

import {useMap} from "mvframe-nuxt/maps";

const metricMap = useMap("asa.metricAll") as { arr: [] };
const columns = [
  {field: "campaignName", title: "Campaign Name"},
  ...metricMap.arr,
];

Use createMetricVTableColumn / createMetricVTableColumns from mvframe-nuxt/composition when code outside MvVTable needs to manually build VTable column objects. Use normalizeMetricFormatRecord(s) only when the caller needs to persist formatted fields onto rows before passing them elsewhere.

import {
  createMetricVTableColumns,
  normalizeMetricFormatRecord,
  type MetricMapItem,
} from "mvframe-nuxt/composition";
import {useMap} from "mvframe-nuxt/maps";

const metricMap = useMap("asa.metricAll") as { arr: MetricMapItem[] };
const columns = createMetricVTableColumns(metricMap, {currencyField: "currency"});
const records = rows.map((row) =>
  normalizeMetricFormatRecord(row, metricMap, {currencyField: "currency"}),
);

Keep metric.formatter as the formatted field key. Do not change dictionary formatter to a function in host code unless a special row-level formatter is truly needed.

MvVTable Flag Columns

Use flag: true when a VTable text column needs a flag prefix. The flag renderer uses the flag-icons SVG assets exposed by the module at /assets/flags, while sorting, copying, searching, and downloading continue to use the text value or formatter value.

const columns = [
  {
    field: "country",
    title: "Country",
    flag: true,
    formatter: "countryFormat",
  },
];

By default, flag: true reads the current column value as an ISO 3166-1 alpha-2 country code and renders /assets/flags/1x1/{code}.svg. Use flagField when the display text and country code come from different fields.

const columns = [
  {
    field: "countryName",
    title: "Country",
    flag: true,
    flagField: "countryCode",
  },
];

Locale values such as en-US are not split into country codes; pass a real alpha-2 code field when a flag is required.

Country cells can contain multiple alpha-2 codes as an array, a JSON array string, or a comma-separated string. VTable displays up to flagLimit flags, shows a tooltip for each hovered flag, and renders im-more-circle when more countries are available. Clicking the more icon opens the built-in country popover.

const columns = [
  {
    field: "countries",
    title: "Countries",
    flag: true,
    flagLimit: 2,
    formatter: "countriesFormat",
  },
];

MvVTable Operation Actions

Use type: "operation" with actions when an operation column needs multiple clickable actions, icons, row-level permission visibility, or per-action hover colors. @cell-click and @cell-action-click receive payload.action.

const actionField = "__actions";
const columns = [
  {
    field: actionField,
    title: "Actions",
    width: 120,
    fixed: "right",
    type: "operation",
    actions: [
      {
        key: "edit",
        iconClass: "imicon im-edit",
        tooltip: "Edit",
        visible: "canEdit",
        hoverColor: "var(--color-primary)",
      },
      {
        key: "del",
        iconClass: "imicon im-delete",
        tooltip: "Delete",
        visible: (record) => record.canDelete === true,
        hoverColor: "var(--color-red)",
      },
    ],
  },
];

function handleCellClick(payload: Record<string, unknown>) {
  if (payload.field !== actionField) return;
  if (payload.action === "edit") {
    // open edit
  }
  if (payload.action === "del") {
    // confirm delete
  }
}

Icon-only actions can set tooltip to a string or tooltip options object. The tooltip is rendered by MvVTable and localized internally through $l.

Normal cells can also define right-aligned cellActions. The table renders the cell text plus action icons, applies visible / disabled per row, and emits cell-action-click with action, record, field, actionRect, and anchor for popovers or dropdowns. A trigger: "edit" action automatically enables the current column editor. editType defaults to "input" and can be set to "select", "list", "textarea", "date", or a registered VTable editor name.

const columns = [
  {
    field: "name",
    title: "Name",
    cellActions: [
      {
        key: "editName",
        iconClass: "imicon im-edit",
        visible: "canEdit",
        trigger: "edit",
        editField: "name",
        editType: "input",
        loading: "_nameEditorLoading",
        editConfig: {
          placeholder: "Name",
        },
      },
      {
        key: "openProfile",
        iconClass: "imicon im-more",
        visible: "canManage",
        trigger: "click",
      },
    ],
  },
  {
    field: "status",
    title: "Status",
    cellActions: [
      {
        key: "editStatus",
        iconClass: "imicon im-edit",
        trigger: "edit",
        editType: "select",
        editOptions: ["Draft", "Active", "Paused"],
      },
    ],
  },
];

function handleCellActionClick(payload: Record<string, unknown>) {
  if (payload.action === "openProfile") {
    currentRecord.value = payload.record;
    profileDrawerVisible.value = true;
  }
}

For built-in dropdown actions, listen to @cell-action-command; the payload includes command, commandItem, record, field, and the original action metadata.

MvEChart

MvEChart wraps ECharts with the mvframe-nuxt resize guard, theme-aware tooltip defaults, and default options for normal cartesian charts.

<MvEChart
  height="320"
  :options="{
    xAxis: { data: ['Mon', 'Tue', 'Wed'] },
    yAxis: {},
    series: [{ name: 'Spend', type: 'line', data: [12, 18, 9] }],
  }"
/>

Default options are merged as defaults first and caller options second. Plain objects merge recursively, arrays are replaced by caller arrays, and xAxis / yAxis arrays merge the default axis object into each caller axis item. Use :merge="false" when the page needs to pass pure ECharts options without mvframe defaults.

Useful props:

  • type: chart type hint for non-cartesian charts such as pie, radar, scatter, gauge, or map.
  • width / height: number and numeric strings are px; CSS strings are used as-is.
  • renderer: canvas by default, svg when vector rendering is required.
  • tool: enables the ECharts toolbox default.
  • notMerge, lazyUpdate, replaceMerge, setOptionOpts: forwarded to setOption.

Local Development

yarn install
yarn dev

The built-in demo lives in demo/ and mounts the local module from src/module.ts. yarn dev starts it at http://127.0.0.1:3600/.

The smaller playground from the initial scaffold remains available:

yarn dev:playground

Roadmap

  • Port more mvframe components into Nuxt runtime components.
  • Add schema-backed API handlers for auth, menu, dictionary, and mock data.
  • Keep request helpers aligned with Nuxt full-stack REST usage.
  • Decide whether to depend on mvframe directly or keep this package as a standalone Nuxt rewrite.