feat(web): antdv-next §3.7 组件能力 spike
create-vue + antdv-next 覆盖侧边栏/展开行/对话框/范围选择/徽章等验收清单。
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
end_of_line = lf
|
||||||
|
max_line_length = 100
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Cypress
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Vitest
|
||||||
|
__screenshots__/
|
||||||
|
|
||||||
|
# Vite
|
||||||
|
*.timestamp-*-*.mjs
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
||||||
|
"plugins": ["eslint", "typescript", "unicorn", "oxc", "vue", "vitest"],
|
||||||
|
"env": {
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"categories": {
|
||||||
|
"correctness": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# apps/web
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||||
|
|
||||||
|
## Recommended Browser Setup
|
||||||
|
|
||||||
|
- Chromium-based browsers (Chrome, Edge, Brave, etc.):
|
||||||
|
- [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)
|
||||||
|
- [Turn on Custom Object Formatter in Chrome DevTools](http://bit.ly/object-formatters)
|
||||||
|
- Firefox:
|
||||||
|
- [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/)
|
||||||
|
- [Turn on Custom Object Formatter in Firefox DevTools](https://fxdx.dev/firefox-devtools-custom-object-formatters/)
|
||||||
|
|
||||||
|
## Type Support for `.vue` Imports in TS
|
||||||
|
|
||||||
|
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||||
|
|
||||||
|
## Customize configuration
|
||||||
|
|
||||||
|
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type-Check, Compile and Minify for Production
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Unit Tests with [Vitest](https://vitest.dev/)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun test:unit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lint with [ESLint](https://eslint.org/)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bun lint
|
||||||
|
```
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { globalIgnores } from 'eslint/config'
|
||||||
|
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
|
||||||
|
import pluginVue from 'eslint-plugin-vue'
|
||||||
|
import pluginVitest from '@vitest/eslint-plugin'
|
||||||
|
import pluginOxlint from 'eslint-plugin-oxlint'
|
||||||
|
import skipFormatting from 'eslint-config-prettier/flat'
|
||||||
|
|
||||||
|
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
|
||||||
|
// import { configureVueProject } from '@vue/eslint-config-typescript'
|
||||||
|
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
|
||||||
|
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
|
||||||
|
|
||||||
|
export default defineConfigWithVueTs(
|
||||||
|
{
|
||||||
|
name: 'app/files-to-lint',
|
||||||
|
files: ['**/*.{vue,ts,mts,tsx}'],
|
||||||
|
},
|
||||||
|
|
||||||
|
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
|
||||||
|
|
||||||
|
...pluginVue.configs['flat/essential'],
|
||||||
|
vueTsConfigs.recommended,
|
||||||
|
|
||||||
|
{
|
||||||
|
...pluginVitest.configs.recommended,
|
||||||
|
files: ['src/**/__tests__/*'],
|
||||||
|
},
|
||||||
|
|
||||||
|
...pluginOxlint.buildFromOxlintConfigFile('.oxlintrc.json'),
|
||||||
|
|
||||||
|
skipFormatting,
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>antdv-next §3.7 能力 spike</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"name": "@new-api-key-usage/web",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "run-p type-check \"build-only {@}\" --",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"test:unit": "vitest",
|
||||||
|
"build-only": "vite build",
|
||||||
|
"type-check": "vue-tsc --build",
|
||||||
|
"lint": "run-s \"lint:*\"",
|
||||||
|
"lint:oxlint": "oxlint . --fix",
|
||||||
|
"lint:eslint": "eslint . --fix --cache",
|
||||||
|
"format": "prettier --write --experimental-cli src/"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@new-api-key-usage/core": "workspace:*",
|
||||||
|
"antdv-next": "^1.6.0",
|
||||||
|
"pinia": "^4.0.3",
|
||||||
|
"vue": "^3.5.42",
|
||||||
|
"vue-router": "^5.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tsconfig/node24": "^24.0.5",
|
||||||
|
"@types/jsdom": "^30.0.0",
|
||||||
|
"@types/node": "^24.13.4",
|
||||||
|
"@vitejs/plugin-vue": "^6.0.8",
|
||||||
|
"@vitejs/plugin-vue-jsx": "^5.1.6",
|
||||||
|
"@vitest/eslint-plugin": "^1.6.27",
|
||||||
|
"@vue/eslint-config-typescript": "^14.9.0",
|
||||||
|
"@vue/test-utils": "^2.5.0",
|
||||||
|
"@vue/tsconfig": "^0.9.1",
|
||||||
|
"eslint": "^10.10.0",
|
||||||
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
"eslint-plugin-oxlint": "~1.82.0",
|
||||||
|
"eslint-plugin-vue": "~10.11.0",
|
||||||
|
"jiti": "^2.7.0",
|
||||||
|
"jsdom": "^30.0.1",
|
||||||
|
"npm-run-all2": "^9.0.3",
|
||||||
|
"oxlint": "~1.82.0",
|
||||||
|
"prettier": "3.9.6",
|
||||||
|
"typescript": "~6.0.0",
|
||||||
|
"vite": "^8.2.2",
|
||||||
|
"vite-plugin-vue-devtools": "^8.2.1",
|
||||||
|
"vitest": "^4.1.11",
|
||||||
|
"vue-eslint-parser": "^10.4.1",
|
||||||
|
"vue-tsc": "^3.3.11"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^22.18.0 || >=24.12.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,168 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* antdv-next §3.7 能力 spike——逐项覆盖《交互设计-总览与共性》清单。
|
||||||
|
* 任一项不覆盖即触发回退(Element Plus / ant-design-vue)并回报作者。
|
||||||
|
*/
|
||||||
|
import { h, ref } from "vue";
|
||||||
|
|
||||||
|
const collapsed = ref(false);
|
||||||
|
const drawerOpen = ref(false);
|
||||||
|
const modalOpen = ref(false);
|
||||||
|
const confirmLoading = ref(false);
|
||||||
|
const range = ref<[string, string] | null>(null);
|
||||||
|
const skKey = ref("");
|
||||||
|
const quotaMode = ref<"usd" | "quota">("usd");
|
||||||
|
const page = ref(1);
|
||||||
|
|
||||||
|
const dataSource = [
|
||||||
|
{
|
||||||
|
key: "1",
|
||||||
|
token_name: "小张-01",
|
||||||
|
billing_source: "subscription",
|
||||||
|
status: "active",
|
||||||
|
quota: 250000,
|
||||||
|
quota_usd: "0.50",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "2",
|
||||||
|
token_name: "小李-02",
|
||||||
|
billing_source: "wallet",
|
||||||
|
status: "disabled",
|
||||||
|
quota: 100000,
|
||||||
|
quota_usd: "0.20",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ title: "名称", dataIndex: "token_name", key: "token_name" },
|
||||||
|
{
|
||||||
|
title: "来源",
|
||||||
|
key: "billing_source",
|
||||||
|
customRender: ({ record }: { record: (typeof dataSource)[0] }) =>
|
||||||
|
h(
|
||||||
|
"a-tag",
|
||||||
|
{ color: record.billing_source === "subscription" ? "blue" : "gold" },
|
||||||
|
() => (record.billing_source === "subscription" ? "订阅" : "充值"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "状态",
|
||||||
|
key: "status",
|
||||||
|
customRender: ({ record }: { record: (typeof dataSource)[0] }) =>
|
||||||
|
h(
|
||||||
|
"a-badge",
|
||||||
|
{
|
||||||
|
status: record.status === "active" ? "success" : "default",
|
||||||
|
text: record.status === "active" ? "启用" : "停用",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ title: "quota", dataIndex: "quota", key: "quota" },
|
||||||
|
{ title: "额度美元", dataIndex: "quota_usd", key: "quota_usd" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function onCopy() {
|
||||||
|
// toast:复制反馈
|
||||||
|
(window as unknown as { antd?: { message?: { success: (m: string) => void } } })
|
||||||
|
.antd?.message?.success?.("已复制");
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRangeChange(vals: [string, string] | null) {
|
||||||
|
range.value = vals;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-layout style="min-height: 100vh">
|
||||||
|
<a-layout-sider v-model:collapsed="collapsed" collapsible>
|
||||||
|
<div style="color: #fff; padding: 16px; font-weight: 600">
|
||||||
|
new-api-key-usage
|
||||||
|
</div>
|
||||||
|
<a-menu theme="dark" mode="inline" default-selected-keys="['spike']">
|
||||||
|
<a-menu-item key="spike">能力 Spike</a-menu-item>
|
||||||
|
<a-menu-item key="overview" disabled>全员总览</a-menu-item>
|
||||||
|
<a-menu-item key="keys" disabled>Key 管理</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-layout-sider>
|
||||||
|
<a-layout>
|
||||||
|
<a-layout-content style="margin: 16px; background: #fff; padding: 16px">
|
||||||
|
<a-alert
|
||||||
|
type="info"
|
||||||
|
show-icon
|
||||||
|
closable
|
||||||
|
message="antdv-next §3.7 能力 spike"
|
||||||
|
description="可折叠侧边栏 / 抽屉 · 密集表格与展开行 · 对话框与二次确认 · message / Alert · DatePicker 范围 · 大号粘贴 · 徽章 / Segmented / Skeleton / Pagination"
|
||||||
|
style="margin-bottom: 16px"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<a-space direction="vertical" style="width: 100%" :size="16">
|
||||||
|
<a-space wrap>
|
||||||
|
<a-button type="primary" @click="drawerOpen = true">打开抽屉</a-button>
|
||||||
|
<a-button @click="modalOpen = true">打开对话框</a-button>
|
||||||
|
<a-button @click="onCopy">message.toast</a-button>
|
||||||
|
<a-popconfirm title="确认停用?" @confirm="onCopy">
|
||||||
|
<a-button danger>二次确认</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
|
||||||
|
<a-space wrap>
|
||||||
|
<a-range-picker
|
||||||
|
style="width: 320px"
|
||||||
|
@change="(_: unknown, vals: [string, string]) => onRangeChange(vals)"
|
||||||
|
/>
|
||||||
|
<a-segmented
|
||||||
|
v-model:value="quotaMode"
|
||||||
|
:options="[
|
||||||
|
{ label: '美元', value: 'usd' },
|
||||||
|
{ label: 'quota', value: 'quota' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</a-space>
|
||||||
|
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="skKey"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="大号粘贴输入框:sk-key 登记对话框用"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<a-table
|
||||||
|
:columns="columns"
|
||||||
|
:data-source="dataSource"
|
||||||
|
:pagination="false"
|
||||||
|
size="small"
|
||||||
|
:scroll="{ x: 800 }"
|
||||||
|
:expand-column-width="48"
|
||||||
|
>
|
||||||
|
<template #expandedRowRender="{ record }">
|
||||||
|
<div style="padding: 8px">
|
||||||
|
来源徽章展开行:{{ record.billing_source }} ·
|
||||||
|
<a-tag color="green">订阅 #3</a-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
|
||||||
|
<a-pagination v-model:current="page" :total="50" show-size-changer />
|
||||||
|
<a-skeleton active :paragraph="{ rows: 2 }" />
|
||||||
|
</a-space>
|
||||||
|
|
||||||
|
<a-drawer
|
||||||
|
v-model:open="drawerOpen"
|
||||||
|
title="可折叠侧边栏 / 抽屉"
|
||||||
|
placement="right"
|
||||||
|
:width="320"
|
||||||
|
>
|
||||||
|
<p>抽屉能力:筛选面板 / 详情。</p>
|
||||||
|
</a-drawer>
|
||||||
|
|
||||||
|
<a-modal
|
||||||
|
v-model:open="modalOpen"
|
||||||
|
title="对话框"
|
||||||
|
:confirm-loading="confirmLoading"
|
||||||
|
@ok="modalOpen = false"
|
||||||
|
>
|
||||||
|
<p>对话框 + 二次确认能力。</p>
|
||||||
|
</a-modal>
|
||||||
|
</a-layout-content>
|
||||||
|
</a-layout>
|
||||||
|
</a-layout>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family:
|
||||||
|
-apple-system,
|
||||||
|
"PingFang SC",
|
||||||
|
"Microsoft YaHei",
|
||||||
|
sans-serif;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { createApp } from "vue";
|
||||||
|
import Antd from "antdv-next";
|
||||||
|
import "antdv-next/dist/reset.css";
|
||||||
|
import App from "./App.vue";
|
||||||
|
import "./assets/main.css";
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
app.use(Antd);
|
||||||
|
app.mount("#app");
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||||
|
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||||
|
"exclude": ["src/**/__tests__/*"],
|
||||||
|
"compilerOptions": {
|
||||||
|
// Extra safety for array and object lookups, but may have false positives.
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
|
||||||
|
// Path mapping for cleaner imports.
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
},
|
||||||
|
|
||||||
|
// `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
|
||||||
|
// Specified here to keep it out of the root directory.
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// TSConfig for modules that run in Node.js environment via either transpilation or type-stripping.
|
||||||
|
{
|
||||||
|
"extends": "@tsconfig/node24/tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"vite.config.*",
|
||||||
|
"vitest.config.*",
|
||||||
|
"cypress.config.*",
|
||||||
|
"playwright.config.*",
|
||||||
|
"eslint.config.*"
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
// Most tools use transpilation instead of Node.js's native type-stripping.
|
||||||
|
// Bundler mode provides a smoother developer experience.
|
||||||
|
"module": "preserve",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
|
||||||
|
// Include Node.js types and avoid accidentally including other `@types/*` packages.
|
||||||
|
"types": ["node"],
|
||||||
|
|
||||||
|
// Disable emitting output during `vue-tsc --build`, which is used for type-checking only.
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
|
||||||
|
// Specified here to keep it out of the root directory.
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
vueJsx(),
|
||||||
|
vueDevTools(),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user