chore: opensource v1-doc

pull/3939/head
tanjinzhou 2021-03-12 15:48:30 +08:00
parent 28325c0507
commit 7c1a14bb5e
822 changed files with 49777 additions and 11 deletions

View File

@ -44,14 +44,6 @@ jobs:
with:
token: ${{ secrets.ACCESS_TOKEN }}
- name: Checkout submodules
uses: actions/checkout@v2
with:
repository: tangjinzhou/antdv-demo
token: ${{ secrets.ACCESS_TOKEN }}
path: antdv-demo
submodules: true
- name: restore cache from package-lock.json
uses: actions/cache@v1
with:

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "antdv-demo"]
path = antdv-demo
url = git@github.com:tangjinzhou/antdv-demo.git

14
antdv-demo/.babelrc Normal file
View File

@ -0,0 +1,14 @@
{
"env": {
"test": {
"presets": [["env", { "targets": { "node": "current" } }]],
"plugins": [
"transform-vue-jsx",
"transform-object-assign",
"transform-object-rest-spread",
"transform-class-properties",
"transform-runtime"
]
}
}
}

11
antdv-demo/.editorconfig Normal file
View File

@ -0,0 +1,11 @@
# 🎨 editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

8
antdv-demo/.eslintignore Normal file
View File

@ -0,0 +1,8 @@
node_modules/
**/*.spec.*
**/style/
*.html
/components/test/*
_site/
dist/
package.json

53
antdv-demo/.eslintrc Normal file
View File

@ -0,0 +1,53 @@
{
"root": true,
"env": {
"browser": true,
"node": true,
"jasmine": true,
"jest": true,
"es6": true
},
"parserOptions": {
"parser": "babel-eslint"
},
"extends": ["plugin:vue/recommended", "prettier"],
"plugins": ["markdown"],
"overrides": [
{
"files": ["**/demo/*.md"],
"processor": "markdown/markdown",
"rules": {
"no-console": "off"
}
}
],
"rules": {
"comma-dangle": [2, "always-multiline"],
"no-var": "error",
"no-console": [2, { "allow": ["warn", "error"] }],
"object-shorthand": 2,
"no-unused-vars": [2, { "ignoreRestSiblings": true, "argsIgnorePattern": "^h$" }],
"no-undef": 2,
"camelcase": "off",
"no-extra-boolean-cast": "off",
"semi": ["error", "always"],
"vue/require-prop-types": "off",
"vue/require-default-prop": "off",
"vue/no-reserved-keys": "off",
"vue/comment-directive": "off",
"vue/prop-name-casing": "off",
"vue/max-attributes-per-line": [
2,
{
"singleline": 20,
"multiline": {
"max": 1,
"allowFirstLine": false
}
}
]
},
"globals": {
"h": true
}
}

109
antdv-demo/.gitignore vendored Normal file
View File

@ -0,0 +1,109 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
site/dev.js
_site/
package-lock.json
testDemo/

7
antdv-demo/.huskyrc Normal file
View File

@ -0,0 +1,7 @@
{
"hooks": {
"pre-commit": "pretty-quick --staged",
"pre-publish": "npm run lint",
"commit-msg": "commitlint -x @commitlint/config-conventional -e $GIT_PARAMS"
}
}

View File

@ -0,0 +1,29 @@
**/*.svg
package.json
lib/
es/
dist/
_site/
coverage/
CNAME
LICENSE
yarn.lock
netlify.toml
yarn-error.log
*.sh
*.snap
.gitignore
.npmignore
.prettierignore
.DS_Store
.editorconfig
.eslintignore
**/*.yml
components/style/color/*.less
**/assets
.gitattributes
.stylelintrc
.vcmrc
.png
.npmrc.template
.huskyrc

14
antdv-demo/.prettierrc Normal file
View File

@ -0,0 +1,14 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"proseWrap": "never",
"overrides": [
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
}
]
}

23
antdv-demo/.stylelintrc Normal file
View File

@ -0,0 +1,23 @@
{
"extends": ["stylelint-config-standard", "stylelint-config-prettier"],
"rules": {
"comment-empty-line-before": null,
"declaration-empty-line-before": null,
"function-comma-newline-after": null,
"function-name-case": null,
"function-parentheses-newline-inside": null,
"function-max-empty-lines": null,
"function-whitespace-after": null,
"indentation": null,
"number-leading-zero": null,
"number-no-trailing-zeros": null,
"rule-empty-line-before": null,
"selector-combinator-space-after": null,
"selector-list-comma-newline-after": null,
"selector-pseudo-element-colon-notation": null,
"unit-no-unknown": null,
"value-list-max-empty-lines": null,
"font-family-no-missing-generic-family-keyword": null,
"no-descending-specificity": null
}
}

View File

@ -0,0 +1,3 @@
module.exports = {
...require('./mock/user'),
};

5
antdv-demo/build.sh Normal file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
rm -rf dist
mkdir dist
./node_modules/.bin/webpack --config webpack.site.config.js
cp dist/index.html index.html

View File

@ -0,0 +1,5 @@
module.exports = {
dev: {
componentName: 'form', // dev components
},
};

213
antdv-demo/build/dev.js Normal file
View File

@ -0,0 +1,213 @@
process.env.ENTRY_INDEX = 'dev';
const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');
const importFresh = require('import-fresh');
const replace = require('json-templater/string');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const devWebpack = require('./webpack.dev.conf');
const configPath = path.join(__dirname, './config.js');
/**
* a-bc-d --> aBcD
* @param {string} s
*/
const camelize = s => s.replace(/-(\w)/g, ($, $1) => $1.toUpperCase());
/**
* radio-group --> radio
* @param {string} s
*/
const getUpper = s => s.replace(/(-[a-z]*)/g, '');
let { componentName } = require('./config').dev;
const componentsInPrototype = ['Modal', 'message', 'notification'];
const MAIN_TEMPLATE = `import 'babel-polyfill';
import Vue from 'vue';
import Vuex from 'vuex';
import VueI18n from 'vue-i18n';
import VueRouter from 'vue-router';
import VueClipboard from 'vue-clipboard2';
import Md from '../components/md';
import Api from '../components/api';
import demoBox from '../components/demoBox';
import demoSort from '../components/demoSort';
import demoContainer from '../components/demoContainer';
import { message, notification } from 'ant-design-vue';
{{importComponents}}
{{importStyles}}
import 'ant-design-vue/es/message/style';
import 'ant-design-vue/es/notification/style';
import Test from '../docs/{{name}}/demo/index.vue';
import zhCN from '../theme/zh-CN';
import enUS from '../theme/en-US';
import './index.less';
Vue.use(Vuex);
Vue.use(VueClipboard);
Vue.use(VueRouter);
Vue.use(VueI18n);
Vue.component(Md.name, Md);
Vue.component(Api.name, Api);
Vue.component('demo-box', demoBox);
Vue.component('demo-sort', demoSort);
Vue.component('demo-container', demoContainer);
Vue.prototype.$message = message;
Vue.prototype.$notification = notification;
Vue.prototype.$info = Modal.info;
Vue.prototype.$success = Modal.success;
Vue.prototype.$error = Modal.error;
Vue.prototype.$warning = Modal.warning;
Vue.prototype.$confirm = Modal.confirm;
Vue.prototype.$destroyAll = Modal.destroyAll;
Vue.prototype.$form = Form;
Vue.use(Modal);
{{install}}
const i18n = new VueI18n({
locale: enUS.locale,
messages: {
[enUS.locale]: { message: enUS.messages },
[zhCN.locale]: { message: zhCN.messages },
},
});
const router = new VueRouter({
mode: 'history',
routes: [{
path: '/test',
component: () => import('../testDemo/index.vue'),
}, {
path: '/*', component: Test
}],
});
const store = new Vuex.Store({
state: {
username: 'zeka',
},
mutations: {
update(state, payload) {
state.username = payload.username;
},
},
});
new Vue({
el: '#app',
i18n,
router,
store,
});
`;
const OUTPUT_PATH = path.join(__dirname, '../site/dev.js');
const generateEntry = components =>
Object.keys(components)
.map(component => `import ${component} from 'ant-design-vue/es/${components[component]}';`)
.join('\n');
const generateStyles = components =>
Object.keys(components)
.map(component => `import 'ant-design-vue/es/${components[component]}/style';`)
.join('\n');
const generateInstall = components =>
Object.keys(components)
.map(component => `Vue.use(${component});`)
.join('\n');
const renderTemplate = name => {
const components = {
Tooltip: 'tooltip', // for DemoBox
Icon: 'icon', // Basic
Form: 'form',
Modal: 'modal',
};
const demoPaths = fs
.readdirSync(path.join(__dirname, `../docs/${name}/demo`))
.map(p => `../docs/${name}/demo/${p}`);
const testPaths = fs
.readdirSync(path.join(__dirname, `../testDemo`))
.map(p => `../testDemo/${p}`);
[...demoPaths, ...testPaths].forEach(demoPath => {
const demo = fs.readFileSync(path.join(__dirname, demoPath)).toString();
const componentsInDemo = demo.match(/a-(\w+(-\w+)*)/g) || [];
componentsInDemo.forEach(name => {
const dirName = name.replace(/^a-/, '');
const componentName = camelize(name).replace(/^a/, '');
const upperComponentDir = getUpper(dirName);
const upperComponentName = upperComponentDir.replace(/^[a-z]/, $ => $.toUpperCase());
const componentPath = path.join(__dirname, `../../components/${dirName}`);
if (fs.existsSync(componentPath)) {
if (componentsInPrototype.includes(componentName)) {
return;
}
components[componentName] = dirName;
} else if (fs.existsSync(path.join(__dirname, `../../components/${upperComponentDir}`))) {
components[upperComponentName] = upperComponentDir;
}
});
});
const importComponents = generateEntry(components);
const importStyles = generateStyles(components);
const install = generateInstall(components);
const template = replace(MAIN_TEMPLATE, {
importComponents,
importStyles,
install,
name,
});
fs.writeFileSync(OUTPUT_PATH, template);
};
function fsExistsSync(path) {
try {
fs.accessSync(path, fs.F_OK);
} catch (e) {
return false;
}
return true;
}
if (!fsExistsSync(path.join(__dirname, '../testDemo/index.vue'))) {
if (!fsExistsSync(path.join(__dirname, '../testDemo'))) {
fs.mkdirSync(path.join(__dirname, '../testDemo'));
}
fs.writeFileSync(path.join(__dirname, '../testDemo/index.vue'), `<template></template>`);
}
let demoWatcher;
chokidar.watch(configPath, { ignoreInitial: true }).on('change', async () => {
({ componentName } = importFresh(configPath).dev);
demoWatcher && (await demoWatcher.close());
demoWatcher = chokidar.watch(path.join(__dirname, `../docs/${componentName}/demo`));
demoWatcher.on('change', () => {
renderTemplate(componentName);
});
renderTemplate(componentName);
});
renderTemplate(componentName);
const compiler = webpack(devWebpack);
const configuration = devWebpack.devServer;
const server = new WebpackDevServer(compiler, configuration);
server.listen(configuration.port);

View File

@ -0,0 +1,45 @@
'use strict';
module.exports = function(modules) {
const plugins = [
require.resolve('babel-plugin-transform-vue-jsx'),
require.resolve('babel-plugin-inline-import-data-uri'),
require.resolve('babel-plugin-transform-es3-member-expression-literals'),
require.resolve('babel-plugin-transform-es3-property-literals'),
require.resolve('babel-plugin-transform-object-assign'),
require.resolve('babel-plugin-transform-object-rest-spread'),
require.resolve('babel-plugin-transform-class-properties'),
];
plugins.push([
require.resolve('babel-plugin-transform-runtime'),
{
polyfill: false,
},
]);
return {
presets: [
[
require.resolve('babel-preset-env'),
{
modules,
targets: {
browsers: [
'last 2 versions',
'Firefox ESR',
'> 1%',
'ie >= 9',
'iOS >= 8',
'Android >= 4',
],
},
},
],
],
plugins,
env: {
test: {
plugins: [require.resolve('babel-plugin-istanbul')],
},
},
};
};

View File

@ -0,0 +1,166 @@
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = function(env) {
const isDev = env === 'development';
return {
test: /\.less$/,
oneOf: [
/* config.module.rule('less').oneOf('vue-modules') */
{
resourceQuery: /module/,
use: [
isDev
? {
loader: 'vue-style-loader',
options: {
sourceMap: isDev,
shadowMode: false,
},
}
: MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: isDev,
importLoaders: 2,
modules: {
localIdentName: '[name]_[local]_[hash:base64:5]',
},
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: isDev,
},
},
{
loader: 'less-loader',
options: {
lessOptions: {
sourceMap: isDev,
modifyVars: {},
javascriptEnabled: true,
},
},
},
],
},
/* config.module.rule('less').oneOf('vue') */
{
resourceQuery: /\?vue/,
use: [
isDev
? {
loader: 'vue-style-loader',
options: {
sourceMap: isDev,
shadowMode: false,
},
}
: MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: isDev,
importLoaders: 2,
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: isDev,
},
},
{
loader: 'less-loader',
options: {
lessOptions: {
sourceMap: isDev,
modifyVars: {},
javascriptEnabled: true,
},
},
},
],
},
/* config.module.rule('less').oneOf('normal-modules') */
{
test: /\.module\.\w+$/,
use: [
isDev
? {
loader: 'vue-style-loader',
options: {
sourceMap: isDev,
shadowMode: false,
},
}
: MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: isDev,
importLoaders: 2,
modules: {
localIdentName: '[name]_[local]_[hash:base64:5]',
},
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: isDev,
},
},
{
loader: 'less-loader',
options: {
lessOptions: {
sourceMap: isDev,
modifyVars: {},
javascriptEnabled: true,
},
},
},
],
},
/* config.module.rule('less').oneOf('normal') */
{
use: [
isDev
? {
loader: 'vue-style-loader',
options: {
sourceMap: isDev,
shadowMode: false,
},
}
: MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: isDev,
importLoaders: 2,
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: isDev,
},
},
{
loader: 'less-loader',
options: {
lessOptions: {
sourceMap: isDev,
modifyVars: {},
javascriptEnabled: true,
},
},
},
],
},
],
};
};

View File

@ -0,0 +1,226 @@
const path = require('path');
const webpack = require('webpack');
const Prism = require('prismjs');
require('prismjs/components/prism-jsx.min.js');
require('prismjs/components/prism-bash.min.js');
require('prismjs/components/prism-json.min.js');
require('prismjs/components/prism-diff.min.js');
require('prismjs/components/prism-less.min.js');
const Token = require('markdown-it/lib/token');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const cheerio = require('cheerio');
const WebpackBar = require('webpackbar');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const getBabelCommonConfig = require('./getBabelCommonConfig');
const babelConfig = getBabelCommonConfig(false);
babelConfig.plugins.push(require.resolve('babel-plugin-syntax-dynamic-import'));
const fetch = (str, tag, scoped) => {
const $ = cheerio.load(str, {
decodeEntities: false,
xmlMode: true,
});
if (!tag) {
return str;
}
if (tag === 'style') {
return scoped
? $(`${tag}[scoped]`).html()
: $(`${tag}`)
.not(`${tag}[scoped]`)
.html();
}
return $(tag).html();
};
/**
* `{{ }}` => `<span>{{</span> <span>}}</span>`
* @param {string} str
* @return {string}
*/
const replaceDelimiters = function(str) {
return str.replace(/({{|}})/g, '<span>$1</span>');
};
/**
* renderHighlight
* @param {string} str
* @param {string} lang
*/
const renderHighlight = function(str, lang) {
if (!(lang && Prism.languages[lang])) {
return '';
}
try {
return replaceDelimiters(Prism.highlight(str, Prism.languages[lang], lang));
} catch (err) {}
};
const md = require('markdown-it')('default', {
html: true,
breaks: true,
highlight: renderHighlight,
}).use(require('markdown-it-anchor'), {
level: 2,
slugify: string =>
string
.trim()
.split(' ')
.join('-'),
permalink: true,
// renderPermalink: (slug, opts, state, permalink) => {},
permalinkClass: 'anchor',
permalinkSymbol: '#',
permalinkBefore: false,
});
// md.renderer.rules.fence = wrap(md.renderer.rules.fence)
const cnReg = new RegExp('<(cn)(?:[^<]|<)+</\\1>', 'g');
const usReg = new RegExp('<(us)(?:[^<]|<)+</\\1>', 'g');
md.core.ruler.push('update_template', function replace({ tokens }) {
let cn = '';
let us = '';
let template = '';
let script = '';
let style = '';
let scopedStyle = '';
let code = '';
let sourceCode = '';
tokens.forEach(token => {
if (token.type === 'html_block') {
if (token.content.match(cnReg)) {
cn = fetch(token.content, 'cn');
token.content = '';
}
if (token.content.match(usReg)) {
us = fetch(token.content, 'us');
token.content = '';
}
}
if (token.type === 'fence' && token.info === 'vue' && token.markup === '```') {
sourceCode = token.content;
code = '```html\n' + token.content + '```';
template = fetch(token.content, 'template');
script = fetch(token.content, 'script');
style = fetch(token.content, 'style');
scopedStyle = fetch(token.content, 'style', true);
token.content = '';
token.type = 'html_block';
}
});
if (template) {
let jsfiddle = {
us,
cn,
sourceCode: Buffer.from(sourceCode).toString('base64'),
};
jsfiddle = md.utils.escapeHtml(JSON.stringify(jsfiddle));
const codeHtml = code ? md.render(code.replace(/@/g, '__at__')).replace(/__at__/g, '@') : '';
const cnHtml = cn ? md.render(cn) : '';
let newContent = `
<template>
<demo-box :jsfiddle="${jsfiddle}">
<template slot="component">${template}</template>
<template slot="description">${cnHtml}</template>
<template slot="us-description">${us ? md.render(us) : ''}</template>
<template slot="code">${Buffer.from(codeHtml).toString('base64')}</template>
</demo-box>
</template>`;
newContent += script
? `
<script>
${script || ''}
</script>
`
: '';
newContent += style ? `<style>${style || ''}</style>` : '';
newContent += scopedStyle ? `<style scoped>${scopedStyle || ''}</style>` : '';
const t = new Token('html_block', '', 0);
t.content = newContent;
tokens.push(t);
}
});
const vueLoaderOptions = {
loaders: {
js: [
{
loader: 'babel-loader',
options: {
presets: ['env'],
plugins: ['transform-vue-jsx', 'transform-object-rest-spread'],
},
},
],
},
};
module.exports = {
mode: 'production',
entry: {
index: [`./site/${process.env.ENTRY_INDEX || 'index'}.js`],
},
module: {
rules: [
{
test: /\.md$/,
use: [
{
loader: 'vue-loader',
options: vueLoaderOptions,
},
{
loader: 'vue-antd-md-loader',
options: Object.assign(md, {
wrapper: 'div',
raw: true,
}),
},
],
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderOptions,
},
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: babelConfig,
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]',
},
},
],
},
resolve:
process.env.NODE_ENV === 'development'
? {
modules: ['node_modules', path.join(__dirname, '../node_modules')],
extensions: ['.js', '.jsx', '.vue', '.md'],
alias: {
vue$: 'vue/dist/vue.esm.js',
'ant-design-vue$': path.join(__dirname, '../../components/index.js'),
'ant-design-vue/es': path.join(__dirname, '../../components'),
'ant-design-vue/lib': path.join(__dirname, '../../components'),
},
}
: {
modules: ['node_modules'],
extensions: ['.js', '.jsx', '.vue', '.md'],
alias: {
vue$: 'vue/dist/vue.esm.js',
},
},
plugins: [
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new VueLoaderPlugin(),
new WebpackBar(),
new HardSourceWebpackPlugin(),
],
};

View File

@ -0,0 +1,59 @@
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const lessLoaderConfig = require('./lessLoaderConfig');
const { createMockMiddleware } = require('umi-mock-middleware');
module.exports = merge(baseWebpackConfig, {
mode: 'development',
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: '/',
filename: 'build.js',
},
module: {
rules: [
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader'],
},
lessLoaderConfig('development'),
],
},
devServer: {
port: process.env.PORT || 3000,
host: '0.0.0.0',
historyApiFallback: {
rewrites: [{ from: /./, to: '/index.html' }],
},
disableHostCheck: true,
hot: true,
open: true,
headers: { 'Access-Control-Allow-Origin': '*' },
// 解析body对接真实服务端环境需要注释掉
before(app) {
// var bodyParser = require("body-parser");
// app.use(bodyParser.json());
if (process.env.MOCK !== 'none') {
app.use(createMockMiddleware());
}
},
proxy: {
'/api': {
target: 'http://localhost:3000',
},
},
},
performance: {
hints: false,
},
devtool: '#source-map',
plugins: [
new HtmlWebpackPlugin({
template: 'public/index.html',
filename: 'index.html',
inject: true,
}),
],
});

View File

@ -0,0 +1,64 @@
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
let lessLoaderConfig = require('./lessLoaderConfig')('production');
module.exports = merge(baseWebpackConfig, {
output: {
path: path.resolve(__dirname, '../_site'),
publicPath: 'https://alicdn.antdv.com/v1/',
filename: '[name].[contenthash:8].js',
chunkFilename: '[contenthash:8].async.js',
},
module: {
rules: [
lessLoaderConfig,
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
},
],
},
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
name: `chunk-vendors`,
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial',
},
common: {
name: `chunk-common`,
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true,
},
},
},
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"',
},
}),
new HtmlWebpackPlugin({
template: './public/index.html',
inject: true,
production: true,
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[id].[contenthash:8].css',
}),
],
});

View File

@ -0,0 +1,124 @@
<script>
const carbonUrls = {
'www.antdv.com': '//cdn.carbonads.com/carbon.js?serve=CK7DL2JW&placement=antdvcom',
// 'tangjinzhou.gitee.io':
// '//cdn.carbonads.com/carbon.js?serve=CK7DL2JN&placement=tangjinzhougiteeio',
// 'ant-design-vue.gitee.io':
// '//cdn.carbonads.com/carbon.js?serve=CK7DL2JN&placement=antdesignvuegiteeio',
'vue.ant.design': '//cdn.carbonads.com/carbon.js?serve=CK7DL2JW&placement=vueantdesign',
};
const carbonUrl =
carbonUrls[location.host] ||
'//cdn.carbonads.com/carbon.js?serve=CK7DL2JW&placement=vueantdesign';
export default {
props: {
isMobile: Boolean,
},
watch: {
$route(e, t) {
let adId = '#carbonads';
// if(isGitee) {
// adId = '#cf';
// }
if (e.path !== t.path && this.$el.querySelector(adId)) {
this.$el.innerHTML = '';
this.load();
}
this.adInterval && clearInterval(this.adInterval);
this.adInterval = setInterval(() => {
if (!this.$el.querySelector(adId)) {
this.$el.innerHTML = '';
this.load();
}
}, 20000);
},
},
mounted() {
this.load();
},
methods: {
load() {
// if(isGitee) {
// axios.get('https://api.codefund.app/properties/162/funder.html?template=horizontal')
// .then(function (response) {
// document.getElementById("codefund-ads").innerHTML = response.data;
// });
// } else
if (carbonUrl) {
const e = document.createElement('script');
e.id = '_carbonads_js';
e.src = carbonUrl;
this.$el.appendChild(e);
}
},
},
render() {
return <div id="carbon-ads" class={this.isMobile ? 'carbon-mobile' : ''} />;
},
};
</script>
<style lang="less">
#carbon-ads {
width: 280px;
float: right;
margin-top: 75px;
position: relative;
right: 0;
bottom: 0;
padding: 0;
overflow: hidden;
z-index: 9;
background-color: #fff;
border-radius: 3px;
font-size: 13px;
background: #f5f5f5;
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
}
#carbonads {
overflow: hidden;
}
#carbon-ads a {
display: inline-block;
color: #7f8c8d;
font-weight: normal;
}
#carbon-ads span {
color: #7f8c8d;
}
#carbon-ads img {
float: left;
padding-right: 10px;
}
#carbon-ads .carbon-img,
#carbon-ads .carbon-text {
display: block;
font-weight: normal;
color: #34495e;
}
#carbon-ads .carbon-text {
padding-top: 6px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 4;
overflow: hidden;
}
#carbon-ads .carbon-poweredby {
color: #aaa;
font-weight: normal;
line-height: 1.2;
margin-top: 6px;
}
#carbon-ads.carbon-mobile {
width: 100%;
position: relative;
right: 0;
bottom: 0;
padding: 0;
margin-bottom: 15px;
margin-top: 5px;
.carbon-img {
float: left;
margin-right: 10px;
}
}
</style>

View File

@ -0,0 +1,36 @@
<script>
export default {
mounted() {
this.load();
},
methods: {
load() {
if (this.scriptDom) {
this.$el.removeChild(this.scriptDom);
}
this.$refs.ins.innerHTML = '';
const e = document.createElement('script');
e.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
e.async = true;
this.$el.appendChild(e);
this.scriptDom = e;
(window.adsbygoogle = window.adsbygoogle || []).push({});
},
},
render() {
return (
<div class="google-ads" id="API">
<ins
ref="ins"
class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-4801326429087140"
data-ad-slot="7647023136"
data-ad-format="auto"
data-full-width-responsive="true"
></ins>
</div>
);
},
};
</script>

View File

@ -0,0 +1,35 @@
<script>
export default {
mounted() {
this.load();
},
methods: {
load() {
if (this.scriptDom) {
this.$el.removeChild(this.scriptDom);
}
this.$refs.ins.innerHTML = '';
const e = document.createElement('script');
e.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
e.async = true;
this.$el.appendChild(e);
this.scriptDom = e;
(window.adsbygoogle = window.adsbygoogle || []).push({});
},
},
render() {
return (
<div class="google-ads" id="api">
<ins
ref="ins"
style="display:block"
data-ad-client="ca-pub-4801326429087140"
data-ad-slot="3952358732"
data-ad-format="auto"
data-full-width-responsive="true"
></ins>
</div>
);
},
};
</script>

View File

@ -0,0 +1,35 @@
<script>
export default {
mounted() {
this.load();
},
methods: {
load() {
if (this.scriptDom) {
this.$el.removeChild(this.scriptDom);
}
this.$refs.ins.innerHTML = '';
const e = document.createElement('script');
e.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
e.async = true;
this.$el.appendChild(e);
this.scriptDom = e;
(window.adsbygoogle = window.adsbygoogle || []).push({});
},
},
render() {
return (
<div class="google-ads">
<ins
ref="ins"
style="display:block"
data-ad-client="ca-pub-4801326429087140"
data-ad-slot="9507921838"
data-ad-format="auto"
data-full-width-responsive="true"
></ins>
</div>
);
},
};
</script>

View File

@ -0,0 +1,34 @@
<script>
export default {
mounted() {
this.load();
},
methods: {
load() {
if (this.scriptDom) {
this.$el.removeChild(this.scriptDom);
}
this.$refs.ins.innerHTML = '';
const e = document.createElement('script');
e.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
e.async = true;
this.$el.appendChild(e);
this.scriptDom = e;
(window.adsbygoogle = window.adsbygoogle || []).push({});
},
},
render() {
return (
<div style="width: 300px;float: right;margin-top: 75px;position: relative;right: 0;bottom: 0;padding: 0;z-index: 9;">
<ins
ref="ins"
class="adsbygoogle"
style="display:inline-block;width:300px;height:100px"
data-ad-client="ca-pub-4801326429087140"
data-ad-slot="2774992529"
></ins>
</div>
);
},
};
</script>

View File

@ -0,0 +1,35 @@
<script>
export default {
mounted() {
this.load();
},
methods: {
load() {
if (this.scriptDom) {
this.$el.removeChild(this.scriptDom);
}
this.$refs.ins.innerHTML = '';
const e = document.createElement('script');
e.src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
e.async = true;
this.$el.appendChild(e);
this.scriptDom = e;
(window.adsbygoogle = window.adsbygoogle || []).push({});
},
},
render() {
return (
<div class="google-ads">
<ins
ref="ins"
style="display:block"
class="adsbygoogle"
data-ad-client="ca-pub-4801326429087140"
data-ad-slot="2425414214"
data-ad-format="auto"
></ins>
</div>
);
},
};
</script>

View File

@ -0,0 +1,28 @@
<template>
<div class="markdown api-container">
<google-ads v-if="showAd" />
<slot v-if="isZhCN" name="cn" />
<slot v-else />
</div>
</template>
<script>
import { isZhCN } from '../utils/util';
import GoogleAds from './GoogleAds';
const showAd = location.host.indexOf('antdv.com') > -1;
export default {
name: 'Api',
components: {
GoogleAds,
},
inject: {
demoContext: { default: {} },
},
data() {
return {
showAd,
isZhCN: isZhCN(this.demoContext.name),
};
},
};
</script>

View File

@ -0,0 +1,230 @@
<template>
<section :id="id" :class="['code-box', codeExpand ? 'expand' : '']">
<section class="code-box-demo">
<template v-if="iframeDemo[iframeDemoKey]">
<div class="browser-mockup with-url">
<iframe :src="iframeDemo[iframeDemoKey]" height="360" />
</div>
</template>
<template v-else>
<slot name="component" />
</template>
</section>
<section class="code-box-meta markdown">
<slot v-if="isZhCN" name="description" />
<slot v-else name="us-description" />
<div class="code-box-actions">
<a-tooltip
:title="copied ? 'Copied!' : 'Copy code'"
:visible="copyTooltipVisible"
@visibleChange="onCopyTooltipVisibleChange"
>
<a-icon
v-clipboard:copy="sourceCode"
v-clipboard:success="handleCodeCopied"
:type="copied && copyTooltipVisible ? 'check' : 'copy'"
class="code-box-code-copy"
/>
</a-tooltip>
<a-tooltip :title="codeExpand ? 'Hide Code' : 'Show Code'">
<span class="code-expand-icon">
<img
width="16"
alt="expand code"
src="https://gw.alipayobjects.com/zos/rmsportal/wSAkBuJFbdxsosKKpqyq.svg"
:class="codeExpand ? 'code-expand-icon-hide' : 'code-expand-icon-show'"
@click="handleCodeExpand"
/>
<img
width="16"
alt="expand code"
src="https://gw.alipayobjects.com/zos/rmsportal/OpROPHYqWmrMDBFMZtKF.svg"
:class="codeExpand ? 'code-expand-icon-show' : 'code-expand-icon-hide'"
@click="handleCodeExpand"
/>
</span>
</a-tooltip>
</div>
</section>
<transition appear :css="false" @enter="enter" @leave="leave">
<section
v-show="codeExpand"
class="highlight-wrapper"
style="position: relative;"
v-html="getCode()"
/>
</transition>
</section>
</template>
<script>
import animate from 'ant-design-vue/es/_util/openAnimation';
import BaseMixin from 'ant-design-vue/es/_util/BaseMixin';
import { isZhCN } from '../utils/util';
import { dev } from '../build/config';
export default {
name: 'DemoBox',
mixins: [BaseMixin],
inject: {
iframeDemo: { default: {} },
demoContext: { default: {} },
},
props: {
jsfiddle: Object,
isIframe: Boolean,
},
data() {
window.test = this;
const { name = '' } = this.demoContext;
const { us, cn, sourceCode } = this.jsfiddle;
const usTitle = (us.split('#### ')[1] || '').split('\n')[0] || '';
const cnTitle = (cn.split('#### ')[1] || '').split('\n')[0] || '';
if (process.env.NODE_ENV !== 'production' && usTitle === '') {
throw new Error(`not have usTitle`);
}
const iframeDemoKey = usTitle
.split(' ')
.join('-')
.toLowerCase();
const id = [
'components',
name.replace(/-cn\/?$/, '') || dev.componentName,
'demo',
...usTitle.split(' '),
]
.join('-')
.toLowerCase();
if (this.demoContext.store) {
const { currentSubMenu } = this.demoContext.store.getState();
this.demoContext.store.setState({
currentSubMenu: [...currentSubMenu, { cnTitle, usTitle, id }],
});
}
return {
codeExpand: false,
isZhCN: isZhCN(name),
copied: false,
copyTooltipVisible: false,
sourceCode: decodeURIComponent(escape(window.atob(sourceCode))),
id,
iframeDemoKey,
isDemo: true,
};
},
methods: {
getCode() {
const { $slots } = this;
return decodeURIComponent(
escape(window.atob(($slots.code && $slots.code[0] && $slots.code[0].text) || '')),
);
},
handleCodeExpand() {
this.codeExpand = !this.codeExpand;
},
enter: animate.enter,
leave: animate.leave,
handleCodeCopied() {
this.setState({ copied: true });
},
onCopyTooltipVisibleChange(visible) {
if (visible) {
this.setState({
copyTooltipVisible: visible,
copied: false,
});
return;
}
this.setState({
copyTooltipVisible: visible,
});
},
},
};
</script>
<style scoped lang="less">
.box-demo {
padding: 0;
border: 1px solid #e9e9e9;
border-radius: 4px;
box-shadow: none;
margin-top: 20px;
margin-bottom: 20px;
}
.box-demo-show {
padding: 20px 25px 30px;
border-bottom: 1px solid #e9e9e9;
}
.box-demo-description {
position: relative;
padding: 17px 16px 15px 20px;
border-radius: 0 0 6px 6px;
-webkit-transition: background-color 0.4s ease;
transition: background-color 0.4s ease;
width: 100%;
font-size: 12px;
&.bordered {
border-bottom: 1px dashed #e9e9e9;
}
h3,
h4 {
position: absolute;
top: -14px;
padding: 1px 8px;
margin-left: -8px;
margin-top: 0;
margin-bottom: 0;
color: #777;
border-radius: 4px;
border-top-left-radius: 0;
background: #fff;
-webkit-transition: background-color 0.4s ease;
transition: background-color 0.4s ease;
.header-anchor {
display: none;
}
}
li {
line-height: 21px;
}
}
.box-demo-code {
-webkit-transition: height 0.2s ease-in-out;
transition: height 0.2s ease-in-out;
overflow: auto;
border-top: 1px dashed #e9e9e9;
pre {
margin: 0;
}
code {
margin: 0;
background: #f7f7f7;
padding: 0.2em 0.4em;
border-radius: 3px;
font-size: 0.9em;
border: 1px solid #eee;
}
}
.btn-toggle {
position: absolute;
right: 16px;
bottom: 17px;
cursor: pointer;
width: 18px;
height: 18px;
font-size: 18px;
line-height: 18px;
color: #999;
i {
-webkit-transition: all 0.3s;
transition: all 0.3s;
}
&.open {
i {
-webkit-transform: rotate(-180deg);
-ms-transform: rotate(-180deg);
transform: rotate(-180deg);
}
}
}
</style>

View File

@ -0,0 +1,82 @@
<template>
<div>
<demo-box :jsfiddle="jsfiddle">
<template slot="component">
<slot />
</template>
<template slot="description">
<div class="demo-description" v-html="cnHtml" />
</template>
<template slot="us-description">
<div class="demo-description" v-html="usHtml" />
</template>
<template slot="code">
{{ codeStr }}
</template>
</demo-box>
</div>
</template>
<script>
import marked from 'marked';
import Prism from 'prismjs';
import 'prismjs/components/prism-jsx.min.js';
import 'prismjs/components/prism-bash.min.js';
const replaceDelimiters = function(str) {
return str.replace(/({{|}})/g, '<span>$1</span>');
};
const renderHighlight = function(str, lang) {
if (!(lang && Prism.languages[lang])) {
return '';
}
try {
return replaceDelimiters(Prism.highlight(str, Prism.languages[lang], lang));
} catch (err) {}
};
const renderer = new marked.Renderer();
renderer.heading = function(text, level) {
return (
'<h' + level + ' id="' + text.replace(/[^\w]+/g, '-') + '">' + text + '</h' + level + '>\n'
);
};
marked.setOptions({
renderer,
gfm: true,
tables: true,
breaks: true,
pedantic: true,
sanitize: true,
smartLists: true,
smartypants: true,
html: true,
highlight: renderHighlight,
});
const cnReg = /<cn>([\S\s\t]*?)<\/cn>/;
const usReg = /<us>([\S\s\t]*?)<\/us>/;
export default {
name: 'DemoContainer',
props: ['code'],
data() {
const cn = this.code.match(cnReg) || [];
const us = this.code.match(usReg) || [];
const cnHtml = marked(cn[1].trim());
const usHtml = marked(us[1].trim());
const sourceCode = this.code
.replace(cn[0], '')
.replace(us[0], '')
.trim();
const codeHtml = marked('```html\n' + sourceCode + '```');
return {
codeStr: window.btoa(unescape(encodeURIComponent(codeHtml))),
cnHtml,
usHtml,
jsfiddle: {
sourceCode: window.btoa(unescape(encodeURIComponent(sourceCode))),
cn: cn[1].trim(),
us: us[1].trim(),
},
};
},
};
</script>

View File

@ -0,0 +1,49 @@
import { Col, Row } from '../../components/grid';
import '../../components/grid/style';
function isEmptyElement(c) {
return !(c.tag || (c.text && c.text.trim() !== ''));
}
function filterEmpty(children = []) {
return children.filter(c => !isEmptyElement(c));
}
export default {
props: {
cols: {
type: [Number, String],
default: 2,
},
},
inject: {
demoContext: { default: {} },
},
render() {
const { cols, $slots } = this;
const isSingleCol = cols === 1;
const leftChildren = [];
const rightChildren = [];
const children = filterEmpty($slots.default);
children.forEach((demo, index) => {
if (index % 2 === 0 || isSingleCol) {
leftChildren.push(demo);
} else {
rightChildren.push(demo);
}
});
return (
<Row gutter={16}>
<Col
span={isSingleCol ? 24 : 12}
class={isSingleCol ? 'code-boxes-col-1-1' : 'code-boxes-col-2-1'}
>
{leftChildren}
</Col>
{isSingleCol ? null : (
<Col class="code-boxes-col-2-1" span={12}>
{rightChildren}
</Col>
)}
</Row>
);
},
};

View File

@ -0,0 +1,136 @@
<script>
export default {
props: {
isCN: Boolean,
},
render() {
const isCN = this.isCN;
return (
<footer id="footer">
<div class="footer-wrap">
<a-row>
<a-col md={6} sm={24} xs={24}>
<div class="footer-center">
<h2>Ant Design</h2>
<div>
<a href="https://github.com/vueComponent/ant-design-vue" target="_blank ">
<span>GitHub</span>
</a>
<span></span>
<span>
<i class="anticon anticon-github"></i>
</span>
</div>
<div>
<a href="https://ant.design/docs/react/introduce-cn" target="_blank">
Ant Design
</a>
<span> - </span>
<span>React</span>
</div>
<div>
<a href="https://github.com/NG-ZORRO/ng-zorro-antd" target="_blank">
Ant Design
</a>
<span> - </span>
<span>Angular</span>
</div>
<div>
<a href="https://github.com/websemantics/awesome-ant-design" target="_blank ">
<span>Awesome Ant Design</span>
</a>
</div>
<div>
<a href="http://library.ant.design/" rel="noopener noreferrer" target="_blank">
AntD Library
</a>
</div>
</div>
</a-col>
<a-col md={6} sm={24} xs={24}>
<div class="footer-center">
<h2>{isCN ? '社区' : 'Community'}</h2>
{isCN ? (
<div>
<a href="https://zhuanlan.zhihu.com/ant-design-vue" target="_blank">
<span>知乎专栏</span>
</a>
</div>
) : (
''
)}
<div>
<a href="https://github.com/vueComponent/ant-design-vue/releases" target="_blank">
<span>{isCN ? '更新记录' : 'Change Log'}</span>
</a>
</div>
<div>
<a
rel="noopener noreferrer"
target="_blank"
href={`https://vuecomponent.github.io/issue-helper/${isCN ? '?lang=zh' : ''}`}
>
<span>{isCN ? '报告 Bug' : 'Bug Report'}</span>
</a>
</div>
</div>
</a-col>
<a-col md={6} sm={24} xs={24}>
<div class="footer-center">
<h2>{isCN ? '友情链接' : 'Links'}</h2>
<div>
<a href="https://cn.vuejs.org/" target="_blank">
Vue
</a>
</div>
<div>
<a href="https://cli.vuejs.org/" target="_blank">
Vue CLI
</a>
</div>
<div>
<a href="http://www.jeecg.com/" rel="noopener noreferrer" target="_blank">
Jeecg Boot
</a>
</div>
</div>
</a-col>
<a-col md={6} sm={24} xs={24}>
<div class="footer-center">
<h2>
<img
alt=""
class="title-icon"
src="https://gw.alipayobjects.com/zos/rmsportal/nBVXkrFdWHxbZlmMbsaH.svg"
/>
<span>{isCN ? '更多产品' : 'More Products'}</span>
</h2>
<div>
<a href="https://antv.alipay.com/" rel="noopener noreferrer" target="_blank">
AntV
</a>
<span> - </span>
<span>{isCN ? '数据可视化' : 'Data Visualization'}</span>
</div>
<div>
<a href="https://eggjs.org/" rel="noopener noreferrer" target="_blank">
Egg
</a>
<span> - </span>
<span>{isCN ? '企业级 Node 开发框架' : 'Enterprise Node Framework'}</span>
</div>
</div>
</a-col>
</a-row>
</div>
<div style="padding: 10px 144px;">
备案号
<a href="http://www.beian.miit.gov.cn/" target="_blank">
浙ICP备19034671号
</a>
</div>
</footer>
);
},
};
</script>

View File

@ -0,0 +1,41 @@
<template>
<div v-show="visible" id="geektime">
<a
href="https://time.geekbang.org/course/intro/100024601?code=KHKYcoBU6vZa8nMglg7AWfDxxi3BWrz9INAzAY3umPk%3D"
target="_blank"
>
<img width="150" alt="Vue 实战教程" src="https://qn.antdv.com/geektime-vue.jpeg" />
</a>
<div class="close" @click="visible = false">
<a-icon type="close" />
</div>
</div>
</template>
<script>
export default {
props: ['isMobile'],
data() {
return {
visible: true,
};
},
};
</script>
<style lang="less" scoped>
#geektime {
position: fixed;
bottom: 10px;
right: 10px;
.close {
position: absolute;
text-align: center;
top: -8px;
right: -8px;
font-size: 16px;
padding: 15px;
color: #6e3041;
}
}
</style>

View File

@ -0,0 +1,60 @@
<template>
<div
v-if="isEffective(effectiveTime)"
id="geektime-ads"
:class="isMobile ? 'geektime-ads-mobile' : ''"
>
<a
href="https://time.geekbang.org/column/intro/154?utm_term=zeusGZFFE&utm_source=app&utm_medium=tangjinzhou"
target="_blank"
>
<img height="100" alt="重学前端" src="https://qn.antdv.com/chongxueqianduan.jpg" />
</a>
</div>
</template>
<script>
import moment from 'moment';
export default {
props: ['isMobile'],
data() {
return {
visible: true,
effectiveTime: {
start: '2019-08-05 17:00:00',
end: '2019-09-05 17:00:00',
},
};
},
methods: {
isEffective({ start, end }) {
return moment().isBetween(start, end);
},
},
};
</script>
<style lang="less" scoped>
#geektime-ads {
width: 266px;
position: fixed;
left: 0;
bottom: 0px;
padding: 0;
overflow: hidden;
z-index: 9;
background-color: #fff;
border-radius: 3px;
font-size: 13px;
background: #f5f5f5;
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
}
#geektime-ads.geektime-ads-mobile {
width: 100%;
position: relative;
right: 0;
bottom: 0;
padding: 0;
margin-bottom: 15px;
}
</style>

View File

@ -0,0 +1,322 @@
<script>
import { isZhCN } from '../utils/util';
import docsearch from 'docsearch.js';
import packageInfo from '../../package.json';
import logo from '../public/logo.svg';
import antDesignVue from '../public/ant-design-vue.svg';
export default {
inject: {
demoContext: { default: {} },
},
props: {
name: String,
searchData: Array,
},
data() {
return {
visibleAdblockBanner: !!this.demoContext.blocked,
value: null,
showTopBanner: !localStorage.getItem('notification-key-2.0'),
};
},
watch: {
'demoContex.blocked': function blocked(val) {
this.visibleAdblockBanner = !!val;
},
},
mounted() {
this.initDocSearch(this.$i18n.locale);
},
methods: {
handleClose(key) {
localStorage.removeItem(`notification-key-${key}`);
localStorage.setItem(`notification-key-${key}`, true);
this.showTopBanner = false;
},
initDocSearch(locale) {
docsearch({
apiKey: '92003c1d1d07beef165b08446f4224a3',
indexName: 'antdv',
inputSelector: '#search-box input',
algoliaOptions: { facetFilters: [isZhCN(locale) ? 'cn' : 'en'] },
transformData(hits) {
hits.forEach(hit => {
hit.url = hit.url.replace('www.antdv.com', window.location.host);
hit.url = hit.url.replace('https:', window.location.protocol);
});
return hits;
},
debug: false, // Set debug to true if you want to inspect the dropdown
});
},
handleClick() {
const name = this.name;
const path = this.$route.path;
const newName = isZhCN(name) ? name.replace(/-cn\/?$/, '') : `${name}-cn`;
this.$router.push({
path: path.replace(name, newName),
});
this.$i18n.locale = isZhCN(name) ? 'en-US' : 'zh-CN';
},
onSelect(val) {
this.$router.push(val);
this.value = val;
},
closeTopBanner() {},
},
render() {
const name = this.name;
const visibleAdblockBanner = false; // this.visibleAdblockBanner;
const isCN = isZhCN(name);
const path = this.$route.path;
const selectedKeys = path === '/jobs/list-cn' ? ['jobs'] : ['components'];
return (
<header id="header">
{visibleAdblockBanner && (
<div class="adblock-banner">
{isZhCN
? '我们检测到你可能使用了 AdBlock 或 Adblock Plus它会影响到正常功能的使用如复制、展开代码等。'
: 'We have detected that you may use AdBlock or Adblock Plus, which will affect the use of normal functions (such as copying, expanding code, etc.)'}
<br />
{isZhCN
? '你可以将 Ant Design Vue 加入白名单,以便我们更好地提供服务。'
: 'You can add Ant Design Vue to the whitelist so that we can provide better services.'}
<CloseOutlined class="close-icon" onClick={() => (this.visibleAdblockBanner = false)} />
</div>
)}
{isCN && this.showTopBanner && (
<div class="global-notification">
<span>
<a href="https://2x.antdv.com/" target="_blank">
2.0 正式版
</a>
&nbsp;已发布更快更小更易用&nbsp;&nbsp;
</span>
<br />
<span style="padding: 5px 0; display: inline-block;">
支持 Vue 3.0全新 Composition API 文档TSJS 双示例
</span>
<br />
<span>
<a href="https://store.antdv.com/pro/" target="_blank">
Vue3 Admin Pro
</a>
&nbsp; 同步更新支持多种布局多标签页暗黑主题等
</span>
<a-icon
type="close"
style="position: absolute;top: 13px;right: 15px;"
onClick={() => this.handleClose('2.0')}
/>
</div>
)}
{!isCN && this.showTopBanner && (
<div class="global-notification">
<span>
<a href="https://2x.antdv.com/" target="_blank">
2.0 release
</a>
&nbsp; Faster, Smaller, Easier&nbsp;&nbsp;
</span>
<br />
<span style="padding: 5px 0; display: inline-block;">
Support Vue 3New Composition API document TS, JS dual examples
</span>
<br />
<span>
<a href="https://store.antdv.com/pro/?lang=en" target="_blank">
Vue3 Admin Pro
</a>
&nbsp; is updated synchronously, supports multiple layouts, dark themes, etc.
</span>
<a-icon
type="close"
style="position: absolute;top: 8px;right: 15px;"
onClick={() => this.handleClose('2.0')}
/>
</div>
)}
<a-row>
<a-col class="header-left" xxl={4} xl={5} lg={5} md={6} sm={24} xs={24}>
<router-link to={{ path: '/' }} id="logo">
<img alt="logo" height="32" src={logo} />
<img alt="logo" height="16" src={antDesignVue} />
</router-link>
<a-button
ghost
size="small"
onClick={this.handleClick}
class="header-lang-button"
key="lang-button"
>
{isCN ? 'English' : '中文'}
</a-button>
</a-col>
<a-col xxl={20} xl={19} lg={19} md={18} sm={0} xs={0}>
<div id="search-box">
<a-icon type="search" />
<a-input
placeholder={isCN ? '搜索组件...' : 'input search text'}
style="width: 200px"
/>
</div>
<span id="github-btn" class="github-btn">
<a class="gh-btn" href="//github.com/vueComponent/ant-design-vue/" target="_blank">
<span class="gh-ico" aria-hidden="true"></span>
<span class="gh-text">Star</span>
</a>
</span>
<a-button
ghost
size="small"
onClick={this.handleClick}
class="header-lang-button"
key="lang-button"
>
{isCN ? 'English' : '中文'}
</a-button>
<a-select size="small" defaultValue={packageInfo.version} class="version">
<a-select-option value={packageInfo.version}>{packageInfo.version}</a-select-option>
<a-select-option value="2.x" onClick={() => (location.href = 'https://2x.antdv.com')}>
2.x
</a-select-option>
</a-select>
<a-menu selectedKeys={selectedKeys} mode="horizontal" class="menu-site" id="nav">
<a-menu-item key="components">
<router-link to="/docs/vue/introduce">{isCN ? '组件' : 'Components'}</router-link>
</a-menu-item>
{isCN ? (
<a-menu-item key="store">
<a
href="https://store.antdv.com/pro/"
target="_blank"
style="position: relative;"
>
商店 <a-badge color="red" style="position: absolute;top: -10px;right: -10px;" />
</a>
</a-menu-item>
) : (
<a-menu-item key="store">
<a
href="https://store.antdv.com/pro/?lang=en"
target="_blank"
style="position: relative;"
>
Store{' '}
<a-badge color="red" style="position: absolute;top: -10px;right: -10px;" />
</a>
</a-menu-item>
)}
{isCN ? (
<a-menu-item key="geektime">
<a
href="https://time.geekbang.org/course/intro/100024601?code=KHKYcoBU6vZa8nMglg7AWfDxxi3BWrz9INAzAY3umPk%3D"
target="_blank"
style="position: relative;"
>
Vue 实战教程
<a-badge color="red" style="position: absolute;top: -10px;right: -10px;" />
</a>
</a-menu-item>
) : null}
<a-menu-item key="sponsor">
<router-link to={{ path: isCN ? '/docs/vue/sponsor-cn/' : '/docs/vue/sponsor/' }}>
{isCN ? '支持我们' : 'Support us'}
</router-link>
</a-menu-item>
<a-sub-menu key="Ecosystem" title={isCN ? '更多' : 'More'}>
<a-menu-item key="pro">
<a target="_blank" href="https://pro.antdv.com">
Pro (Admin)
</a>
</a-menu-item>
<a-menu-item key="vip">
<a target="_blank" href="https://store.antdv.com/pro/">
Pro For VIP
</a>
</a-menu-item>
<a-menu-item key="design">
<router-link
to={{ path: isCN ? '/docs/vue/download-cn/' : '/docs/vue/download/' }}
>
{isCN ? '设计资源' : 'Design Resources'}
</router-link>
</a-menu-item>
<a-menu-item key="vscode">
<a
target="_blank"
href="https://marketplace.visualstudio.com/items?itemName=ant-design-vue.vscode-ant-design-vue-helper"
>
VS Code Extension
</a>
</a-menu-item>
<a-menu-item key="awesome">
<a target="_blank" href="https://github.com/vueComponent/ant-design-vue-awesome">
Awesome
</a>
</a-menu-item>
<a-menu-item key="wechat">
<a-popover placement="right">
<template slot="content">
<img
width="160"
height="160"
alt="wechat"
src="https://qn.antdv.com/wechat.jpeg"
/>
</template>
<a>{isCN ? '微信' : 'WeChat'}</a>
</a-popover>
</a-menu-item>
<a-menu-item key="qq">
<a-popover placement="right">
<template slot="content">
<img width="160" height="160" alt="qq" src="https://qn.antdv.com/qq.png" />
</template>
<a>{isCN ? 'QQ(217490093)' : 'QQ(217490093)'}</a>
</a-popover>
</a-menu-item>
</a-sub-menu>
</a-menu>
</a-col>
</a-row>
</header>
);
},
};
</script>
<style scope>
.adblock-banner {
position: relative;
z-index: 100;
min-width: 1000px;
padding: 16px;
line-height: 28px;
color: #8590a6;
text-align: center;
background-color: #ebebeb;
}
.close-icon {
position: absolute;
top: 15px;
right: 15px;
}
.global-notification {
text-align: center;
background: #001529;
padding: 20px 0;
font-size: 16px;
position: fixed;
bottom: 0;
width: 100%;
color: #fff;
z-index: 99;
}
.global-notification a {
color: #177ddc;
}
</style>

View File

@ -0,0 +1,30 @@
<script>
// import * as AllDemo from '../demo'
export default {
props: {
name: String,
hash: String,
},
provide() {
return {
demoContext: this,
};
},
render() {
// const name = this.name
// const titleMap = {}
// for (const [title] of Object.entries(AllDemo)) {
// const key = `${title.replace(/(\B[A-Z])/g, '-$1').toLowerCase()}`
// titleMap[key] = title
// }
// const Demo = AllDemo[titleMap[name.replace(/-cn\/?$/, '')]]
// const hash = this.$route.hash.replace('#', '')
return (
<div id="iframe-page">
<router-view></router-view>
</div>
);
},
};
</script>

View File

@ -0,0 +1,374 @@
<script>
import { enquireScreen } from 'enquire-js';
import AllDemo from '../site/demo';
import Header from './header';
import Footer from './footer';
// import Geektime from './geektime';
import GeektimeAds from './geektime_ads';
import RightBottomAd from './right_bottom_ad';
import Sponsors from './sponsors';
import zhCN from 'ant-design-vue/es/locale-provider/zh_CN';
import enUS from 'ant-design-vue/es/locale-provider/default';
import sortBy from 'lodash/sortBy';
import { isZhCN } from '../utils/util';
import { Provider, create } from '../../components/_util/store';
import NProgress from 'nprogress';
import MobileMenu from '../../components/vc-drawer/src';
import TopAd from './top_ad';
import GoogleAds from './GoogleAds';
const docsList = [
{ key: 'introduce', enTitle: 'Ant Design of Vue', title: 'Ant Design of Vue' },
{ key: 'getting-started', enTitle: 'Getting Started', title: '快速上手' },
{ key: 'use-with-vue-cli', enTitle: 'Use in vue-cli', title: '在 vue-cli 中使用' },
{ key: 'customize-theme', enTitle: 'Customize Theme', title: '定制主题' },
{ key: 'changelog', enTitle: 'Change Log', title: '更新日志' },
{ key: 'i18n', enTitle: 'Internationalization', title: '国际化' },
{ key: 'faq', enTitle: 'FAQ', title: '常见问题' },
{ key: 'sponsor', enTitle: 'Sponsor', title: '支持我们' },
{ key: 'download', enTitle: 'Download Design Resources', title: '下载设计资源' },
];
const isGitee = window.location.host.indexOf('gitee.io') > -1;
const showAd = false; // location.host.indexOf('antdv.com') > -1;
export default {
provide() {
return {
demoContext: this,
};
},
props: {
name: String,
showDemo: Boolean,
showApi: Boolean,
},
data() {
this.store = create({
currentSubMenu: [],
});
this.subscribe();
let blocked = false;
setTimeout(() => {
const div = document.createElement('div');
div.className = 'adsbox';
document.body.appendChild(div);
blocked = 'none' === getComputedStyle(div).display;
}, 300);
return {
showSideBars: true,
currentSubMenu: [],
sidebarHeight: document.documentElement.offsetHeight,
isMobile: false,
blocked,
};
},
watch: {
'$route.path'() {
this.store.setState({ currentSubMenu: [] });
this.addSubMenu();
},
},
beforeDestroy() {
if (this.unsubscribe) {
this.unsubscribe();
}
clearTimeout(this.timer);
if (this.resizeEvent) {
this.resizeEvent.remove();
}
if (this.debouncedResize && this.debouncedResize.cancel) {
this.debouncedResize.cancel();
}
},
mounted() {
if (isGitee) {
this.$info({
title: '提示',
content: '访问国内镜像站点的用户请访问 antdv.com 站点',
okText: '立即跳转',
onOk() {
location.href = 'https://www.antdv.com';
},
});
}
this.$nextTick(() => {
this.addSubMenu();
const nprogressHiddenStyle = document.getElementById('nprogress-style');
if (nprogressHiddenStyle) {
this.timer = setTimeout(() => {
nprogressHiddenStyle.parentNode.removeChild(nprogressHiddenStyle);
}, 0);
}
enquireScreen(b => {
this.isMobile = !!b;
});
});
},
methods: {
addSubMenu() {
if (this.$route.path.indexOf('/docs/vue/') !== -1) {
this.$nextTick(() => {
const menus = [];
const doms = [...this.$refs.doc.querySelectorAll(['h2', 'h3'])];
doms.forEach(dom => {
const id = dom.id;
if (id) {
const title = dom.textContent.split('#')[0].trim();
menus.push({ cnTitle: title, usTitle: title, id });
}
});
this.currentSubMenu = menus;
});
}
},
subscribe() {
const { store } = this;
this.unsubscribe = store.subscribe(() => {
this.currentSubMenu = this.store.getState().currentSubMenu;
});
},
getSubMenu(isCN) {
const currentSubMenu = this.currentSubMenu;
const lis = [];
currentSubMenu.forEach(({ cnTitle, usTitle, id }, index) => {
const title = isCN ? cnTitle : usTitle;
lis.push(<a-anchor-link key={id + index} href={`#${id}`} title={title} />);
});
const showApi = this.$route.path.indexOf('/components/') !== -1;
return (
<a-anchor offsetTop={100} class="demo-anchor">
{lis}
{showApi ? <a-anchor-link key="API" title="API" href="#API" /> : ''}
</a-anchor>
);
},
getDocsMenu(isCN, pagesKey) {
const docsMenu = [];
docsList.forEach(({ key, enTitle, title }) => {
const k = isCN ? `${key}-cn` : key;
pagesKey.push({ name: k, url: `/docs/vue/${k}/`, title: isCN ? title : enTitle });
docsMenu.push(
<a-menu-item key={k}>
<router-link to={`/docs/vue/${k}/`}>{isCN ? title : enTitle}</router-link>
</a-menu-item>,
);
});
return docsMenu;
},
resetDocumentTitle(component, name, isCN) {
let titleStr = 'Ant Design Vue';
if (component) {
const { subtitle, title } = component;
const componentName = isCN ? subtitle + ' ' + title : title;
titleStr = componentName + ' - ' + titleStr;
} else {
const currentKey = docsList.filter(item => {
return item.key === name;
});
if (currentKey.length) {
titleStr = (isCN ? currentKey[0]['title'] : currentKey[0]['enTitle']) + ' - ' + titleStr;
}
}
document.title = titleStr;
},
mountedCallback() {
NProgress.done();
document.documentElement.scrollTop = 0;
},
},
render() {
const name = this.name;
const isCN = isZhCN(name);
const titleMap = {};
const menuConfig = {
General: [],
Layout: [],
Navigation: [],
'Data Entry': [],
'Data Display': [],
Feedback: [],
Other: [],
};
const pagesKey = [];
let prevPage = null;
let nextPage = null;
const searchData = [];
for (const [title, d] of Object.entries(AllDemo)) {
const type = d.type || 'Other';
const key = `${title.replace(/(\B[A-Z])/g, '-$1').toLowerCase()}`;
titleMap[key] = title;
AllDemo[title].key = key;
menuConfig[type] = menuConfig[type] || [];
menuConfig[type].push(d);
}
const docsMenu = this.getDocsMenu(isCN, pagesKey);
const reName = name.replace(/-cn\/?$/, '');
const MenuGroup = [];
for (const [type, menus] of Object.entries(menuConfig)) {
const MenuItems = [];
sortBy(menus, ['title']).forEach(({ title, subtitle, key }) => {
const linkValue = isCN
? [<span>{title}</span>, <span class="chinese">{subtitle}</span>]
: [<span>{title}</span>];
if (isCN) {
key = `${key}-cn`;
}
pagesKey.push({
name: key,
url: `/components/${key}/`,
title: isCN ? `${title} ${subtitle}` : title,
});
searchData.push({
title,
subtitle,
url: `/components/${key}/`,
});
MenuItems.push(
<a-menu-item key={key}>
<router-link to={`/components/${key}/`}>{linkValue}</router-link>
</a-menu-item>,
);
});
MenuGroup.push(<a-menu-item-group title={type}>{MenuItems}</a-menu-item-group>);
}
pagesKey.forEach((item, index) => {
if (item.name === name) {
prevPage = pagesKey[index - 1];
nextPage = pagesKey[index + 1];
}
});
let locale = zhCN;
if (!isCN) {
locale = enUS;
}
const config = AllDemo[titleMap[reName]];
this.resetDocumentTitle(config, reName, isCN);
const { isMobile, $route } = this;
return (
<div class="page-wrapper">
<Header searchData={searchData} name={name} />
<a-config-provider locale={locale}>
<div class="main-wrapper">
<a-row>
{isMobile ? (
<MobileMenu ref="sidebar" wrapperClassName="drawer-wrapper">
<a-menu
class="aside-container menu-site"
selectedKeys={[name]}
defaultOpenKeys={['Components']}
inlineIndent={40}
mode="inline"
>
{docsMenu}
<a-sub-menu title={`Components(${searchData.length})`} key="Components">
{MenuGroup}
</a-sub-menu>
</a-menu>
</MobileMenu>
) : (
<a-col
ref="sidebar"
class="site-sidebar main-menu"
xxl={4}
xl={5}
lg={5}
md={6}
sm={8}
xs={12}
>
<a-affix>
<section class="main-menu-inner">
<Sponsors isCN={isCN} />
<a-menu
class="aside-container menu-site"
selectedKeys={[name]}
defaultOpenKeys={['Components']}
inlineIndent={40}
mode="inline"
>
{docsMenu}
<a-sub-menu title={`Components(${searchData.length})`} key="Components">
{MenuGroup}
</a-sub-menu>
</a-menu>
</section>
</a-affix>
</a-col>
)}
<a-col xxl={20} xl={19} lg={19} md={18} sm={24} xs={24}>
<section class="main-container main-container-component">
<TopAd isCN={isCN} />
{showAd ? <GeektimeAds isMobile={isMobile} /> : null}
{!isMobile ? (
<div class={['toc-affix', isCN ? 'toc-affix-cn' : '']} style="width: 150px;">
{this.getSubMenu(isCN)}
</div>
) : null}
{this.showDemo ? (
<Provider store={this.store} key={isCN ? 'cn' : 'en'}>
<router-view
class={`demo-cols-${config.cols || 2}`}
{...{
directives: [
{
name: 'mountedCallback',
value: this.mountedCallback,
},
],
}}
></router-view>
</Provider>
) : (
''
)}
{this.showApi ? (
<div class="markdown api-container" ref="doc">
<router-view
{...{
directives: [
{
name: 'mountedCallback',
value: this.mountedCallback,
},
],
}}
></router-view>
{showAd ? <GoogleAds key={`GoogleAds_${$route.path}`} /> : null}
</div>
) : (
''
)}
</section>
<section class="prev-next-nav">
{prevPage ? (
<router-link class="prev-page" to={`${prevPage.url}`}>
<a-icon type="left" />
&nbsp;&nbsp;{prevPage.title}
</router-link>
) : (
''
)}
{nextPage ? (
<router-link class="next-page" to={`${nextPage.url}`}>
{nextPage.title}&nbsp;&nbsp;
<a-icon type="right" />
</router-link>
) : (
''
)}
</section>
<Footer ref="footer" isCN={isCN} />
</a-col>
</a-row>
</div>
</a-config-provider>
{name.indexOf('back-top') === -1 ? <a-back-top /> : null}
<RightBottomAd isCN={isCN} isMobile={isMobile} />
</div>
);
},
};
</script>

View File

@ -0,0 +1,51 @@
<template>
<div class="markdown" v-html="marked(text)" />
</template>
<script>
import marked from 'marked';
import { isZhCN } from '../utils/util';
const renderer = new marked.Renderer();
renderer.heading = function(text, level) {
return (
'<h' + level + ' id="' + text.replace(/[^\w]+/g, '-') + '">' + text + '</h' + level + '>\n'
);
};
marked.setOptions({
renderer,
gfm: true,
tables: true,
breaks: true,
pedantic: true,
sanitize: true,
smartLists: true,
smartypants: true,
});
export default {
name: 'Md',
props: {
cn: String,
us: String,
},
inject: {
demoContext: { default: {} },
},
data() {
let text = '';
const { cn, us } = this;
if (this.$slots.default && this.$slots.default[0] && this.$slots.default[0].text) {
text = this.$slots.default[0].text;
} else {
text = isZhCN(this.demoContext.name) ? cn : us;
}
text = text || '';
text = text
.split('\n')
.map(t => t.trim())
.join('\n');
return {
marked,
text,
};
},
};
</script>

View File

@ -0,0 +1,57 @@
<template>
<div id="right-bottom">
<img v-if="isCN" width="150" alt="官方公众号" src="https://qn.antdv.com/wechat.jpeg">
<div v-if="isMobile" class="close" @click="visible = false">
<a-icon type="close" />
</div>
<!-- <span v-if="isCN">广</span> -->
</div>
</template>
<script>
import moment from 'moment';
const isEffective = (start, end) => {
return moment().isBetween(start, end);
};
export default {
components: {},
props: ['isCN', 'isMobile'],
data() {
return {
visible: true,
ads: [
{
alt: 'geektime',
img: 'https://qn.antdv.com/geektime-web-small.jpg',
href: 'http://gk.link/a/10l8O',
visible: isEffective('2020-09-03 10:00:00', '2020-10-04 10:00:00'),
},
{
alt: 'powerproject',
img: 'https://qn.antdv.com/powerproject.jpeg?v=20200327',
href: 'http://www.powerproject.com.cn',
visible: isEffective('2020-03-27 17:00:00', '2020-09-28 17:00:00'),
},
],
};
},
};
</script>
<style lang="less" scoped>
#right-bottom {
position: fixed;
bottom: 10px;
right: 10px;
width: 150px;
.close {
position: absolute;
text-align: center;
top: -8px;
right: -8px;
font-size: 16px;
padding: 15px;
color: #6e3041;
}
}
</style>

View File

@ -0,0 +1,66 @@
<template>
<div class="snd-ad">
<div class="sponsorsWrap">
<span v-if="!isCN" class="sponsorsTitle">
{{ isCN ? '赞助商' : 'Sponsors' }}
</span>
<ul>
<!-- <li class="sponsorsItem">
<a href="https://tipe.io/?ref=ant-design-vue" target="_blank">
<img height="51" src="https://cdn.tipe.io/tipe/tipe-cat-no-text.svg" alt="tipe" />
</a>
</li> -->
<li v-if="isCN && isEffective(effectiveTime.kkb)" class="sponsorsItem">
<a href="https://datayi.cn/w/Y9J3M2vR" target="_blank">
<img height="66" src="https://qn.antdv.com/kaikeba_ssr.jpeg" alt="kaikeba">
</a>
<span style="position: absolute; top: 0px;right: 10px">广告</span>
</li>
<!-- <li class="sponsorsItem">
<a-button type="primary" ghost style="font-size: 12px" @click="handleClick">
{{ isCN ? '成为赞助商' : 'Become a Sponsor' }}
</a-button>
</li> -->
</ul>
</div>
<a-modal v-model="visible" title="成为赞助商" @ok="visible = false">
如果您有品牌推广活动推广招聘推广社区合作等需求欢迎联系我们成为赞助商<br>
您的广告将出现在 And Design Vue 文档所有子页面及 GitHub Readme 等页面<br>
咨询邮箱<a href="mailto:antdv@foxmail.com">antdv@foxmail.com</a><br>
</a-modal>
</div>
</template>
<script>
import moment from 'moment';
export default {
props: ['isCN'],
data() {
return {
top: 50,
effectiveTime: {
bmatch: {
start: '2019-03-11',
end: '2019-06-11',
},
kkb: {
start: '2020-11-16 22:00:00',
end: '2021-05-17 22:00:00',
},
},
visible: false,
};
},
methods: {
isEffective({ start, end }) {
return moment().isBetween(start, end);
},
handleClick() {
if (this.isCN) {
this.visible = true;
} else {
window.open('https://opencollective.com/ant-design-vue#sponsor');
}
},
},
};
</script>

View File

@ -0,0 +1,56 @@
<template>
<div>
<template v-if="ads.length">
<a-carousel autoplay>
<template v-for="ad in ads">
<a :key="ad.href" :href="ad.href" target="_blank">
<img style="width: 100%; max-width: 1200px;" :alt="ad.alt || ''" :src="ad.img" />
</a>
</template>
</a-carousel>
</template>
<template v-else-if="showGoogleAd">
<GoogleAdsTop :key="`GoogleAdsTop_${$route.path}`" />
</template>
</div>
</template>
<script>
import moment from 'moment';
import GoogleAdsTop from './GoogleAdsTop';
const isEffective = (start, end) => {
return moment().isBetween(start, end);
};
export default {
components: {
GoogleAdsTop,
},
props: ['isCN', 'isMobile'],
data() {
return {
showGoogleAd: location.host.indexOf('antdv.com') > -1,
cnAds: [
{
img: `https://yidengfe.com/launches/01/yd.png?v=${Date.now()}`,
href: 'https://yidengfe.com/launches/01/yd.html',
visible: isEffective('2020-09-11 17:00:00', '2021-03-11 17:00:00'),
},
].filter(ad => ad.visible),
enAds: [
{
img: 'https://qn.antdv.com/TheBigRichGroup.png',
href: 'https://thebigrichgroup.com/',
visible: isEffective('2020-09-18 17:00:00', '2021-10-11 17:00:00'),
},
].filter(ad => ad.visible),
};
},
computed: {
ads() {
return this.isCN ? this.cnAds : this.enAds;
},
},
};
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,38 @@
<cn>
#### 基本
最简单的用法。
</cn>
<us>
#### basic
The simplest usage.
</us>
```vue
<template>
<div>
<a-affix :offset-top="top">
<a-button type="primary" @click="top += 10">
Affix top
</a-button>
</a-affix>
<br />
<a-affix :offset-bottom="bottom">
<a-button type="primary" @click="bottom += 10">
Affix bottom
</a-button>
</a-affix>
</div>
</template>
<script>
export default {
data() {
return {
top: 10,
bottom: 10,
};
},
};
</script>
```

View File

@ -0,0 +1,44 @@
<script>
import Basic from './basic';
import Onchange from './on-change';
import Target from './target';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# Affix 固钉
将页面元素钉在可视范围
## 何时使用
当内容区域比较长需要滚动页面时这部分内容对应的操作或者导航需要在滚动范围内始终展现常用于侧边菜单和按钮组合
页面可视范围过小时慎用此功能以免遮挡页面内容
## 代码演示`,
us: `# Affix
Make an element stick to viewport.
## When To Use
When user browses a long web page, some content need to stick to the viewport. This is common for menus and actions.
Please note that Affix should not cover other content on the page, especially when the size of the viewport is small.
## Examples `,
};
export default {
category: 'Components',
subtitle: '固钉',
zhType: '导航',
type: 'Navigation',
title: 'Affix',
render() {
return (
<div>
<md cn={md.cn} us={md.us} />
<demo-sort>
<Basic />
<Onchange />
<Target />
</demo-sort>
<api>
<CN slot="cn" />
<US />
</api>
</div>
);
},
};
</script>

View File

@ -0,0 +1,26 @@
<cn>
#### 固定状态改变的回调
可以获得是否固定的状态。
</cn>
<us>
#### Callback
Callback with affixed state.
</us>
```vue
<template>
<a-affix :offset-top="120" @change="change">
<a-button>120px to affix top</a-button>
</a-affix>
</template>
<script>
export default {
methods: {
change(affixed) {
console.log(affixed);
},
},
};
</script>
```

View File

@ -0,0 +1,34 @@
<cn>
#### 滚动容器
`target` 设置 `Affix` 需要监听其滚动事件的元素,默认为 `window`
</cn>
<us>
#### Container to scroll.
Set a `target` for 'Affix', which is listen to scroll event of target element (default is `window`).
</us>
```vue
<template>
<div id="components-affix-demo-target" ref="container" class="scrollable-container">
<div class="background">
<a-affix :target="() => this.$refs.container">
<a-button type="primary">
Fixed at the top of container
</a-button>
</a-affix>
</div>
</div>
</template>
<style>
#components-affix-demo-target.scrollable-container {
height: 100px;
overflow-y: scroll;
}
#components-affix-demo-target .background {
padding-top: 60px;
height: 300px;
background-image: url('https://zos.alipayobjects.com/rmsportal/RmjwQiJorKyobvI.jpg');
}
</style>
```

View File

@ -0,0 +1,29 @@
## API
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| offsetBottom | Offset from the bottom of the viewport (in pixels) | number | - | |
| offsetTop | Offset from the top of the viewport (in pixels) | number | 0 | |
| target | Specifies the scrollable area DOM node | () => HTMLElement | () => window | |
### events
| Events Name | Description | Arguments | Version |
| ----------- | ---------------------------------------- | ----------------- | ------- |
| change | Callback for when Affix state is changed | Function(affixed) |
**Note:** Children of `Affix` must not have the property `position: absolute`, but you can set `position: absolute` on `Affix` itself:
```html
<a-affix :style="{ position: 'absolute', top: y, left: x}">
...
</a-affix>
```
## FAQ
### Affix bind container with `target`, sometime move out of container.
We don't listen window scroll for performance consideration.
Related issues[#3938](https://github.com/ant-design/ant-design/issues/3938) [#5642](https://github.com/ant-design/ant-design/issues/5642) [#16120](https://github.com/ant-design/ant-design/issues/16120)

View File

@ -0,0 +1,29 @@
## API
| 成员 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | | |
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | | |
| target | 设置 `Affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | () => HTMLElement | () => window | |
### 事件
| 事件名称 | 说明 | 回调参数 | 版本 |
| -------- | ---------------------------- | ----------------- | ---- |
| change | 固定状态改变时触发的回调函数 | Function(affixed) | 无 | |
**注意:**`Affix` 内的元素不要使用绝对定位,如需要绝对定位的效果,可以直接设置 `Affix` 为绝对定位:
```html
<a-affix :style="{ position: 'absolute', top: y, left: x}">
...
</a-affix>
```
## FAQ
### Affix 使用 `target` 绑定容器时,元素会跑到容器外。
从性能角度考虑,我们只监听容器滚动事件。
相关 issue[#3938](https://github.com/ant-design/ant-design/issues/3938) [#5642](https://github.com/ant-design/ant-design/issues/5642) [#16120](https://github.com/ant-design/ant-design/issues/16120)

View File

@ -0,0 +1,27 @@
<cn>
#### 顶部公告
页面顶部通告形式,默认有图标且`type` 为 'warning'。
</cn>
<us>
#### Banner
Display Alert as a banner at top of page.
</us>
```vue
<template>
<div>
<a-alert message="Warning text" banner />
<br />
<a-alert
message="Very long warning text warning text text text text text text text"
banner
closable
/>
<br />
<a-alert :show-icon="false" message="Warning text without icon" banner />
<br />
<a-alert type="error" message="Error text" banner />
</div>
</template>
```

View File

@ -0,0 +1,15 @@
<cn>
#### 基本
最简单的用法,适用于简短的警告提示。
</cn>
<us>
#### basic
The simplest usage for short messages.
</us>
```vue
<template>
<a-alert message="Success Text" type="success" />
</template>
```

View File

@ -0,0 +1,38 @@
<cn>
#### 可关闭的警告提示
显示关闭按钮,点击可关闭警告提示。
</cn>
<us>
#### Closable
To show close button.
</us>
```vue
<template>
<div>
<a-alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
closable
@close="onClose"
/>
<a-alert
message="Error Text"
description="Error Description Error Description Error Description Error Description Error Description Error Description"
type="error"
closable
@close="onClose"
/>
</div>
</template>
<script>
export default {
methods: {
onClose(e) {
console.log(e, 'I was closed.');
},
},
};
</script>
```

View File

@ -0,0 +1,15 @@
<cn>
#### 自定义关闭
可以自定义关闭,自定义的文字会替换原先的关闭 `Icon`
</cn>
<us>
#### Customized Close Text
Replace the default icon with customized text.
</us>
```vue
<template>
<a-alert message="Info Text" type="info" close-text="Close Now" />
</template>
```

View File

@ -0,0 +1,63 @@
<cn>
#### 自定义图标
可口的图标让信息类型更加醒目。
</cn>
<us>
#### Custom Icon
Decent icon make information more clear and more friendly.
</us>
```vue
<template>
<div>
<a-alert message="showIcon = false" type="success">
<a-icon slot="icon" type="smile" />
</a-alert>
<a-alert message="Success Tips" type="success" show-icon>
<a-icon slot="icon" type="smile" />
</a-alert>
<a-alert message="Informational Notes" type="info" show-icon>
<a-icon slot="icon" type="smile" />
</a-alert>
<a-alert message="Warning" type="warning" show-icon>
<a-icon slot="icon" type="smile" />
</a-alert>
<a-alert message="Error" type="error" show-icon>
<a-icon slot="icon" type="smile" />
</a-alert>
<a-alert
message="Success Tips"
description="Detailed description and advices about successful copywriting."
type="success"
show-icon
>
<a-icon slot="icon" type="smile" />
</a-alert>
<a-alert
message="Informational Notes"
description="Additional description and informations about copywriting."
type="info"
show-icon
>
<a-icon slot="icon" type="smile" />
</a-alert>
<a-alert
message="Warning"
description="This is a warning notice about copywriting."
type="warning"
show-icon
>
<a-icon slot="icon" type="smile" />
</a-alert>
<a-alert
message="Error"
description="This is an error message about copywriting."
type="error"
show-icon
>
<a-icon slot="icon" type="smile" />
</a-alert>
</div>
</template>
```

View File

@ -0,0 +1,36 @@
<cn>
#### 含有辅助性文字介绍
含有辅助性文字介绍的警告提示。
</cn>
<us>
#### Description
Additional description for alert message.
</us>
```vue
<template>
<div>
<a-alert message="Success Text" type="success">
<p slot="description">
Success Description <span style="color: red">Success</span> Description Success Description
</p>
</a-alert>
<a-alert
message="Info Text"
description="Info Description Info Description Info Description Info Description"
type="info"
/>
<a-alert
message="Warning Text"
description="Warning Description Warning Description Warning Description Warning Description"
type="warning"
/>
<a-alert
message="Error Text"
description="Error Description Error Description Error Description Error Description"
type="error"
/>
</div>
</template>
```

View File

@ -0,0 +1,44 @@
<cn>
#### 图标
可口的图标让信息类型更加醒目。
</cn>
<us>
#### Icon
Decent icon make information more clear and more friendly.
</us>
```vue
<template>
<div>
<a-alert message="Success Tips" type="success" show-icon />
<a-alert message="Informational Notes" type="info" show-icon />
<a-alert message="Warning" type="warning" show-icon />
<a-alert message="Error" type="error" show-icon />
<a-alert
message="Success Tips"
description="Detailed description and advices about successful copywriting."
type="success"
show-icon
/>
<a-alert
message="Informational Notes"
description="Additional description and informations about copywriting."
type="info"
show-icon
/>
<a-alert
message="Warning"
description="This is a warning notice about copywriting."
type="warning"
show-icon
/>
<a-alert
message="Error"
description="This is an error message about copywriting."
type="error"
show-icon
/>
</div>
</template>
```

View File

@ -0,0 +1,61 @@
<script>
import Banner from './banner';
import Basic from './basic';
import Closable from './closable';
import CloseText from './close-text';
import Description from './description';
import Icon from './icon';
import Style from './style';
import SmoothClosed from './smooth-closed';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# Alert 警告提示
警告提示展现需要关注的信息
## 何时使用
- 当某个页面需要向用户显示警告的信息时
- 非浮层的静态展现形式始终展现不会自动消失用户可以点击关闭
## 代码演示`,
us: `# Alert
Alert component for feedback.
## When To Use
- When you need to show alert messages to users.
- When you need a persistent static container which is closable by user actions.
## Examples
`,
};
export default {
category: 'Components',
subtitle: '警告提示',
type: 'Feedback',
zhType: '反馈',
title: 'Alert',
render() {
return (
<div id="components-alert-demo">
<md cn={md.cn} us={md.us} />
<demo-sort>
<Banner />
<Basic />
<Closable />
<CloseText />
<Description />
<Icon />
<Style />
<SmoothClosed />
</demo-sort>
<api>
<CN slot="cn" />
<US />
</api>
</div>
);
},
};
</script>
<style>
#components-alert-demo .ant-alert {
margin-bottom: 16px;
}
</style>

View File

@ -0,0 +1,37 @@
<cn>
#### 平滑地卸载
平滑、自然的卸载提示。
</cn>
<us>
#### Smoothly Unmount
Smoothly and unaffectedly unmount Alert.
</us>
```vue
<template>
<div>
<a-alert
v-if="visible"
message="Alert Message Text"
type="success"
closable
:after-close="handleClose"
/>
</div>
</template>
<script>
export default {
data() {
return {
visible: true,
};
},
methods: {
handleClose() {
this.visible = false;
},
},
};
</script>
```

View File

@ -0,0 +1,20 @@
<cn>
#### 四种样式
共有四种样式 `success`、`info`、`warning`、`error`。
</cn>
<us>
#### More types
There are 4 types of Alert: `success`, `info`, `warning`, `error`.
</us>
```vue
<template>
<div>
<a-alert message="Success Text" type="success" />
<a-alert message="Info Text" type="info" />
<a-alert message="Warning Text" type="warning" />
<a-alert message="Error Text" type="error" />
</div>
</template>
```

View File

@ -0,0 +1,19 @@
## API
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| afterClose | Called when close animation is finished | () => void | - | - |
| banner | Whether to show as banner | boolean | false | - |
| closable | Whether Alert can be closed | boolean | - | - |
| closeText | Close text to show | string\|slot | - | - |
| description | Additional content of Alert | string\|slot | - | - |
| icon | Custom icon, effective when `showIcon` is `true` | vnode \| slot | - | - |
| message | Content of Alert | string\|slot | - | - |
| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true | - |
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` | - |
### events
| Events Name | Description | Arguments | Version |
| ----------- | ----------------------------- | ----------------------- | ------- |
| close | Callback when Alert is closed | (e: MouseEvent) => void | - |

View File

@ -0,0 +1,19 @@
## API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - | - |
| banner | 是否用作顶部公告 | boolean | false | - |
| closable | 默认不显示关闭按钮 | boolean | 无 | - |
| closeText | 自定义关闭按钮 | string\|slot | 无 | - |
| description | 警告提示的辅助性文字介绍 | string\|slot | 无 | - |
| icon | 自定义图标,`showIcon` 为 `true` 时有效 | vnode \| slot | - | - |
| message | 警告提示内容 | string\|slot | 无 | - |
| showIcon | 是否显示辅助图标 | boolean | false`banner` 模式下默认值为 true | - |
| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | string | `info``banner` 模式下默认值为 `warning` | - |
### 事件
| 事件名称 | 说明 | 回调参数 | 版本 |
| -------- | -------------------- | ----------------------- | ---- |
| close | 关闭时触发的回调函数 | (e: MouseEvent) => void | - |

View File

@ -0,0 +1,27 @@
<cn>
#### 基本
最简单的用法。
</cn>
<us>
#### basic
The simplest usage.
</us>
```vue
<template>
<a-anchor>
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link
href="#components-anchor-demo-basic"
title="Basic demo with Target"
target="_blank"
/>
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
</template>
```

View File

@ -0,0 +1,31 @@
<cn>
#### 自定义锚点高亮
自定义锚点高亮。
</cn>
<us>
#### Customize the anchor highlight
Customize the anchor highlight.
</us>
```vue
<template>
<a-anchor :affix="false" :get-current-anchor="getCurrentAnchor">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
</template>
<script>
export default {
methods: {
getCurrentAnchor() {
return '#components-anchor-demo-static';
},
},
};
</script>
```

View File

@ -0,0 +1,61 @@
<script>
import Basic from './basic';
import Static from './static';
import OnClick from './onClick';
import CustomizeHighlight from './customizeHighlight';
import OnChange from './onChange';
import TargetOffset from './targetOffset';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# Anchor 锚点
用于跳转到页面指定位置
## 何时使用
需要展现当前页面上可供跳转的锚点链接以及快速在锚点之间跳转
## 代码演示`,
us: `# Anchor
Hyperlinks to scroll on one page.
## When To Use
For displaying anchor hyperlinks on page and jumping between them.
## Examples
`,
};
export default {
category: 'Components',
subtitle: '锚点',
cols: 2,
type: 'Other',
zhType: '其他',
title: 'Anchor',
render() {
return (
<div id="components-anchor-demo">
<md cn={md.cn} us={md.us} />
<demo-sort>
<Basic />
<Static />
<OnClick />
<CustomizeHighlight />
<OnChange />
<TargetOffset />
</demo-sort>
<api>
<CN slot="cn" />
<US />
</api>
</div>
);
},
};
</script>
<style>
#components-anchor-demo .ant-affix {
z-index: 11;
}
</style>

View File

@ -0,0 +1,31 @@
<cn>
#### 监听锚点链接改变
监听锚点链接改变
</cn>
<us>
#### Listening for anchor link change
Listening for anchor link change.
</us>
```vue
<template>
<a-anchor :affix="false" @change="onChange">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
</template>
<script>
export default {
methods: {
onChange(link) {
console.log('Anchor:OnChange', link);
},
},
};
</script>
```

View File

@ -0,0 +1,32 @@
<cn>
#### 自定义 click 事件
点击锚点不记录历史。
</cn>
<us>
#### Customize the click event
Clicking on an anchor does not record history.
</us>
```vue
<template>
<a-anchor :affix="false" @click="handleClick">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
</template>
<script>
export default {
methods: {
handleClick(e, link) {
e.preventDefault();
console.log(link);
},
},
};
</script>
```

View File

@ -0,0 +1,22 @@
<cn>
#### 静态位置
不浮动,状态不随页面滚动变化。
</cn>
<us>
#### Static
Do not change state when page is scrolling.
</us>
```vue
<template>
<a-anchor :affix="false">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
</template>
```

View File

@ -0,0 +1,34 @@
<cn>
#### 设置锚点滚动偏移量
锚点目标滚动到屏幕正中间。
</cn>
<us>
#### Set Anchor scroll offset
Anchor target scroll to screen center.
</us>
```vue
<template>
<a-anchor :target-offset="targetOffset">
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
<a-anchor-link href="#API" title="API">
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
<a-anchor-link href="#Link-Props" title="Link Props" />
</a-anchor-link>
</a-anchor>
</template>
<script>
export default {
data() {
return {
targetOffset: undefined,
};
},
mounted() {
this.targetOffset = window.innerHeight / 2;
},
};
</script>
```

View File

@ -0,0 +1,31 @@
## API
### Anchor Props
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| affix | Fixed mode of Anchor | boolean | true | |
| bounds | Bounding distance of anchor area | number | 5(px) | |
| getContainer | Scrolling container | () => HTMLElement | () => window | |
| offsetBottom | Pixels to offset from bottom when calculating position of scroll | number | - | |
| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 | |
| showInkInFixed | Whether show ink-balls in Fixed mode | boolean | false | |
| wrapperClass | The class name of the container | string | - | |
| wrapperStyle | The style of the container | object | - | |
| getCurrentAnchor | Customize the anchor highlight | () => string | - | 1.5.0 |
| targetOffset | Anchor scroll offset, default as `offsetTop`, [example](#components-anchor-demo-targetOffset) | number | `offsetTop` | 1.5.0 |
### Events
| Events Name | Description | Arguments | Version |
| --- | --- | --- | --- |
| click | set the handler to handle `click` event | Function(e: Event, link: Object) | |
| change | Listening for anchor link change | (currentActiveLink: string) => void | | 1.5.0 |
### Link Props
| Property | Description | Type | Default | Version |
| -------- | ----------------------------------------- | ------------ | ------- | ------- |
| href | target of hyperlink | string | | |
| title | content of hyperlink | string\|slot | | |
| target | Specifies where to display the linked URL | string | | 1.5.0 |

View File

@ -0,0 +1,31 @@
## API
### Anchor Props
| 成员 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| affix | 固定模式 | boolean | true | |
| bounds | 锚点区域边界 | number | 5(px) | |
| getContainer | 指定滚动的容器 | () => HTMLElement | () => window | |
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | | |
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | | |
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false | |
| wrapperClass | 容器的类名 | string | - | |
| wrapperStyle | 容器样式 | object | - | |
| getCurrentAnchor | 自定义高亮的锚点 | () => string | - | 1.5.0 |
| targetOffset | 锚点滚动偏移量,默认与 offsetTop 相同,[例子](#components-anchor-demo-targetOffset) | number | `offsetTop` | 1.5.0 |
### 事件
| 事件名称 | 说明 | 回调参数 | 版本 |
| -------- | ---------------------- | ----------------------------------- | ---- |
| click | `click` 事件的 handler | Function(e: Event, link: Object) | |
| change | 监听锚点链接改变 | (currentActiveLink: string) => void | | 1.5.0 |
### Link Props
| 成员 | 说明 | 类型 | 默认值 | 版本 |
| ------ | -------------------------------- | ------------ | ------ | ----- |
| href | 锚点链接 | string | | |
| title | 文字内容 | string\|slot | | |
| target | 该属性指定在何处显示链接的资源。 | string | | 1.5.0 |

View File

@ -0,0 +1,49 @@
<cn>
#### 基本使用
基本使用。通过 dataSource 设置自动完成的数据源
</cn>
<us>
#### Basic Usage
Basic Usage, set datasource of autocomplete with `dataSource` property.
</us>
```vue
<template>
<a-auto-complete
v-model="value"
:data-source="dataSource"
style="width: 200px"
placeholder="input here"
@select="onSelect"
@search="onSearch"
@change="onChange"
/>
</template>
<script>
export default {
data() {
return {
value: '',
dataSource: [],
};
},
watch: {
value(val) {
console.log('value', val);
},
},
methods: {
onSearch(searchText) {
this.dataSource = !searchText ? [] : [searchText, searchText.repeat(2), searchText.repeat(3)];
},
onSelect(value) {
console.log('onSelect', value);
},
onChange(value) {
console.log('onChange', value);
},
},
};
</script>
```

View File

@ -0,0 +1,149 @@
<cn>
#### 查询模式 - 确定类目
查询模式 - 确定类目
</cn>
<us>
#### Lookup-Patterns - Certain Category
Lookup-Patterns - Certain Category
</us>
```vue
<template>
<div class="certain-category-search-wrapper" style="width: 250px">
<a-auto-complete
class="certain-category-search"
dropdown-class-name="certain-category-search-dropdown"
:dropdown-match-select-width="false"
:dropdown-style="{ width: '300px' }"
size="large"
style="width: 100%"
placeholder="input here"
option-label-prop="value"
>
<template slot="dataSource">
<a-select-opt-group v-for="group in dataSource" :key="group.title">
<span slot="label">
{{ group.title }}
<a
style="float: right"
href="https://www.google.com/search?q=antd"
target="_blank"
rel="noopener noreferrer"
>more
</a>
</span>
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.title">
{{ opt.title }}
<span class="certain-search-item-count">{{ opt.count }} people</span>
</a-select-option>
</a-select-opt-group>
<a-select-option key="all" disabled class="show-all">
<a
href="https://www.google.com/search?q=ant-design-vue"
target="_blank"
rel="noopener noreferrer"
>
View all results
</a>
</a-select-option>
</template>
<a-input>
<a-icon slot="suffix" type="search" class="certain-category-icon" />
</a-input>
</a-auto-complete>
</div>
</template>
<script>
const dataSource = [
{
title: 'Libraries',
children: [
{
title: 'AntDesign',
count: 10000,
},
{
title: 'AntDesign UI',
count: 10600,
},
],
},
{
title: 'Solutions',
children: [
{
title: 'AntDesign UI',
count: 60100,
},
{
title: 'AntDesign',
count: 30010,
},
],
},
{
title: 'Articles',
children: [
{
title: 'AntDesign design language',
count: 100000,
},
],
},
];
export default {
data() {
return {
dataSource,
};
},
};
</script>
<style>
.certain-category-search-dropdown .ant-select-dropdown-menu-item-group-title {
color: #666;
font-weight: bold;
}
.certain-category-search-dropdown .ant-select-dropdown-menu-item-group {
border-bottom: 1px solid #f6f6f6;
}
.certain-category-search-dropdown .ant-select-dropdown-menu-item {
padding-left: 16px;
}
.certain-category-search-dropdown .ant-select-dropdown-menu-item.show-all {
text-align: center;
cursor: default;
}
.certain-category-search-dropdown .ant-select-dropdown-menu {
max-height: 300px;
}
</style>
<style scoped>
.certain-category-search-wrapper
>>> .certain-category-search.ant-select-auto-complete
.ant-input-affix-wrapper
.ant-input-suffix {
right: 12px;
}
.certain-category-search-wrapper >>> .certain-search-item-count {
position: absolute;
color: #999;
right: 16px;
}
.certain-category-search-wrapper
>>> .certain-category-search.ant-select-focused
.certain-category-icon {
color: #108ee9;
}
.certain-category-search-wrapper >>> .certain-category-icon {
color: #6e6e6e;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
font-size: 16px;
}
</style>
```

View File

@ -0,0 +1,47 @@
<cn>
#### 自定义输入组件
自定义输入组件。
</cn>
<us>
#### Customize Input Component
Customize Input Component
</us>
```vue
<template>
<a-auto-complete
:data-source="dataSource"
style="width: 200px"
@search="handleSearch"
@select="onSelect"
>
<a-textarea
placeholder="input here"
class="custom"
style="height: 50px"
@keypress="handleKeyPress"
/>
</a-auto-complete>
</template>
<script>
export default {
data() {
return {
dataSource: [],
};
},
methods: {
onSelect(value) {
console.log('onSelect', value);
},
handleSearch(value) {
this.dataSource = !value ? [] : [value, value + value, value + value + value];
},
handleKeyPress(ev) {
console.log('handleKeyPress', ev);
},
},
};
</script>
```

View File

@ -0,0 +1,51 @@
<script>
import Basic from './basic';
import CertainCategory from './certain-category';
import Custom from './custom';
import NonCaseSensitive from './non-case-sensitive';
import Options from './options';
import UncertainCategory from './uncertain-category';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# AutoComplete 自动完成
输入框自动完成功能
## 何时使用
需要自动完成时
## 代码演示`,
us: `# AutoComplete
Autocomplete function of input field.
## When To Use
When there is a need for autocomplete functionality.
## Examples
`,
};
export default {
category: 'Components',
subtitle: '自动完成',
type: 'Data Entry',
zhType: '数据录入',
cols: 2,
title: 'AutoComplete',
render() {
return (
<div>
<md cn={md.cn} us={md.us} />
<demo-sort>
<Basic />
<CertainCategory />
<Custom />
<NonCaseSensitive />
<Options />
<UncertainCategory />
</demo-sort>
<api>
<CN slot="cn" />
<US />
</api>
</div>
);
},
};
</script>

View File

@ -0,0 +1,36 @@
<cn>
#### 不区分大小写
不区分大小写的 AutoComplete
</cn>
<us>
#### Non-case-sensitive AutoComplete
A non-case-sensitive AutoComplete
</us>
```vue
<template>
<a-auto-complete
:data-source="dataSource"
style="width: 200px"
placeholder="input here"
:filter-option="filterOption"
/>
</template>
<script>
export default {
data() {
return {
dataSource: ['Burns Bay Road', 'Downing Street', 'Wall Street'],
};
},
methods: {
filterOption(input, option) {
return (
option.componentOptions.children[0].text.toUpperCase().indexOf(input.toUpperCase()) >= 0
);
},
},
};
</script>
```

View File

@ -0,0 +1,41 @@
<cn>
#### 自定义选项
也可以直接传递slot="dataSource"的Option
</cn>
<us>
#### Customized
You could pass `slot="dataSource` as children of `AutoComplete`, instead of using `dataSource`
</us>
```vue
<template>
<a-auto-complete style="width: 200px" placeholder="input here" @search="handleSearch">
<template slot="dataSource">
<a-select-option v-for="email in result" :key="email">
{{ email }}
</a-select-option>
</template>
</a-auto-complete>
</template>
<script>
export default {
data() {
return {
result: [],
};
},
methods: {
handleSearch(value) {
let result;
if (!value || value.indexOf('@') >= 0) {
result = [];
} else {
result = ['gmail.com', '163.com', 'qq.com'].map(domain => `${value}@${domain}`);
}
this.result = result;
},
},
};
</script>
```

View File

@ -0,0 +1,120 @@
<cn>
#### 查询模式 - 不确定类目
查询模式 - 不确定类目
</cn>
<us>
#### Lookup-Patterns - Uncertain Category
Lookup-Patterns - Uncertain Category
</us>
```vue
<template>
<div class="global-search-wrapper" style="width: 300px">
<a-auto-complete
class="global-search"
size="large"
style="width: 100%"
placeholder="input here"
option-label-prop="title"
@select="onSelect"
@search="handleSearch"
>
<template slot="dataSource">
<a-select-option v-for="item in dataSource" :key="item.category" :title="item.category">
Found {{ item.query }} on
<a
:href="`https://s.taobao.com/search?q=${item.query}`"
target="_blank"
rel="noopener noreferrer"
>
{{ item.category }}
</a>
<span className="global-search-item-count">{{ item.count }} results</span>
</a-select-option>
</template>
<a-input>
<a-button
slot="suffix"
style="margin-right: -12px"
class="search-btn"
size="large"
type="primary"
>
<a-icon type="search" />
</a-button>
</a-input>
</a-auto-complete>
</div>
</template>
<script>
export default {
data() {
return {
dataSource: [],
};
},
methods: {
onSelect(value) {
console.log('onSelect', value);
},
handleSearch(value) {
this.dataSource = value ? this.searchResult(value) : [];
},
getRandomInt(max, min = 0) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
searchResult(query) {
return new Array(this.getRandomInt(5))
.join('.')
.split('.')
.map((item, idx) => ({
query,
category: `${query}${idx}`,
count: this.getRandomInt(200, 100),
}));
},
},
};
</script>
<style>
.global-search-wrapper {
padding-right: 50px;
}
.global-search {
width: 100%;
}
.global-search.ant-select-auto-complete .ant-select-selection--single {
margin-right: -46px;
}
.global-search.ant-select-auto-complete .ant-input-affix-wrapper .ant-input:not(:last-child) {
padding-right: 62px;
}
.global-search.ant-select-auto-complete .ant-input-affix-wrapper .ant-input-suffix button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.global-search-item {
display: flex;
}
.global-search-item-desc {
flex: auto;
text-overflow: ellipsis;
overflow: hidden;
}
.global-search-item-count {
flex: none;
}
</style>
```

View File

@ -0,0 +1,41 @@
## API
```html
<a-auto-complete :dataSource="dataSource" />
```
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| allowClear | Show clear button, effective in multiple mode only. | boolean | false | |
| autoFocus | get focus when component mounted | boolean | false | |
| backfill | backfill selected item the input when using keyboard | boolean | false | |
| slot="default" (for customize input element) | customize input element | HTMLInputElement / HTMLTextAreaElement | `<Input />` | |
| dataSource | Data source for autocomplete | slot \| [DataSourceItemType](https://github.com/vueComponent/ant-design-vue/blob/724d53b907e577cf5880c1e6742d4c3f924f8f49/components/auto-complete/index.vue#L9)\[] | | |
| dropdownMenuStyle | additional style applied to dropdown menu | object | | 1.5.0 |
| defaultActiveFirstOption | Whether active first option by default | boolean | true | |
| defaultValue | Initial selected option. | string\|string\[]\| - | |
| disabled | Whether disabled select | boolean | false | |
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. | boolean or function(inputValue, option) | true | |
| optionLabelProp | Which prop value of option will render as content of select. | string | `children` | |
| placeholder | placeholder of input | string | - | |
| value(v-model) | selected option | string\|string\[]\|{ key: string, label: string\|vNodes }\|Array&lt;{ key: string, label: string\|vNodes }> | - | |
| defaultOpen | Initial open state of dropdown | boolean | - | |
| open | Controlled open state of dropdown | boolean | - | |
### events
| Events Name | Description | Arguments | Version |
| --- | --- | --- | --- |
| change | Called when select an option or input value change, or value of input is changed | function(value) | |
| blur | Called when leaving the component. | function() | |
| focus | Called when entering the component | function() | |
| search | Called when searching items. | function(value) | - | |
| select | Called when a option is selected. param is option's value and option instance. | function(value, option) | |
| dropdownVisibleChange | Call when dropdown open | function(open) | |
## Methods
| Name | Description | Version |
| ------- | ------------ | ------- |
| blur() | remove focus | |
| focus() | get focus | |

View File

@ -0,0 +1,41 @@
## API
```html
<a-auto-complete :dataSource="dataSource" />
```
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| allowClear | 支持清除, 单选模式有效 | boolean | false | |
| autoFocus | 自动获取焦点 | boolean | false | |
| backfill | 使用键盘选择选项的时候把选中项回填到输入框中 | boolean | false | |
| slot="default" (自定义输入框) | 自定义输入框 | HTMLInputElement / HTMLTextAreaElement | `<Input />` | |
| dataSource | 自动完成的数据源 | slot \| [DataSourceItemType](https://github.com/vueComponent/ant-design-vue/blob/724d53b907e577cf5880c1e6742d4c3f924f8f49/components/auto-complete/index.vue#L9)\[] | | |
| dropdownMenuStyle | dropdown 菜单自定义样式 | object | | 1.5.0 |
| defaultActiveFirstOption | 是否默认高亮第一个选项。 | boolean | true | |
| defaultValue | 指定默认选中的条目 | string\|string\[]\| 无 | |
| disabled | 是否禁用 | boolean | false | |
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | boolean or function(inputValue, option) | true | |
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value`。 | string | `children` | |
| placeholder | 输入框提示 | string \| slot | - | |
| value(v-model) | 指定当前选中的条目 | string\|string\[]\|{ key: string, label: string\|vNodes }\|Array&lt;{ key: string, label: string\|vNodes }> | 无 | |
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
| open | 是否展开下拉菜单 | boolean | - | |
### 事件
| 事件名称 | 说明 | 回调参数 | 版本 |
| --- | --- | --- | --- |
| change | 选中 option或 input 的 value 变化时,调用此函数 | function(value) |
| blur | 失去焦点时的回调 | function() |
| focus | 获得焦点时的回调 | function() |
| search | 搜索补全项的时候调用 | function(value) |
| select | 被选中时调用,参数为选中项的 value 值 | function(value, option) |
| dropdownVisibleChange | 展开下拉菜单的回调 | function(open) |
## 方法
| 名称 | 描述 | 版本 |
| ------- | -------- | ---- |
| blur() | 移除焦点 |
| focus() | 获取焦点 |

View File

@ -0,0 +1,22 @@
<cn>
#### 带徽标的头像
通常用于消息提示。
</cn>
<us>
#### With Badge
Usually used for reminders and notifications.
</us>
```vue
<template>
<div>
<span style="margin-right:24px">
<a-badge :count="1"><a-avatar shape="square" icon="user"/></a-badge>
</span>
<span>
<a-badge dot><a-avatar shape="square" icon="user"/></a-badge>
</span>
</div>
</template>
```

View File

@ -0,0 +1,29 @@
<cn>
#### 基本
头像有三种尺寸,两种形状可选。
</cn>
<us>
#### basic
Three sizes and two shapes are available.
</us>
```vue
<template>
<div>
<div>
<a-avatar :size="64" icon="user" />
<a-avatar size="large" icon="user" />
<a-avatar icon="user" />
<a-avatar size="small" icon="user" />
</div>
<br />
<div>
<a-avatar shape="square" :size="64" icon="user" />
<a-avatar shape="square" size="large" icon="user" />
<a-avatar shape="square" icon="user" />
<a-avatar shape="square" size="small" icon="user" />
</div>
</div>
</template>
```

View File

@ -0,0 +1,49 @@
<cn>
#### 自动调整字符大小
对于字符型的头像,当字符串较长时,字体大小可以根据头像宽度自动调整。
</cn>
<us>
#### Autoset Font Size
For letter type Avatar, when the letters are too long to display, the font size can be automatically adjusted according to the width of the Avatar.
</us>
```vue
<template>
<div>
<a-avatar
shape="square"
size="large"
:style="{ backgroundColor: color, verticalAlign: 'middle' }"
>
{{ avatarValue }}
</a-avatar>
<a-button
size="small"
:style="{ marginLeft: 16, verticalAlign: 'middle' }"
@click="changeValue"
>
改变
</a-button>
</div>
</template>
<script>
const UserList = ['U', 'Lucy', 'Tom', 'Edward'];
const colorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
export default {
data() {
return {
avatarValue: UserList[0],
color: colorList[0],
};
},
methods: {
changeValue() {
const index = UserList.indexOf(this.avatarValue);
this.avatarValue = index < UserList.length - 1 ? UserList[index + 1] : UserList[0];
this.color = index < colorList.length - 1 ? colorList[index + 1] : colorList[0];
},
},
};
</script>
```

View File

@ -0,0 +1,47 @@
<script>
import Basic from './basic';
import Badge from './badge';
import Type from './type';
import Dynamic from './dynamic';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# Avatar头像
用来代表用户或事物支持图片图标或字符展示
## 设计师专属
安装 [Kitchen Sketch 插件 <EFBFBD>](https://kitchen.alipay.com).
## 代码演示`,
us: `# Avatar
Avatars can be used to represent people or objects. It supports images, 'Icon's, or letters.
## Examples
`,
};
export default {
category: 'Components',
subtitle: '头像',
type: 'Data Display',
zhType: '数据展示',
title: 'Avatar',
render() {
return (
<div>
<md cn={md.cn} us={md.us} />
<demo-sort>
<Basic />
<Badge />
<Type />
<Dynamic />
</demo-sort>
<api>
<template slot="cn">
<CN />
</template>
<US />
</api>
</div>
);
},
};
</script>

View File

@ -0,0 +1,27 @@
<cn>
#### 类型
支持三种类型图片、Icon 以及字符,其中 Icon 和字符型可以自定义图标颜色及背景色。
</cn>
<us>
#### Type
Image, Icon and letter are supported, and the latter two kinds avatar can have custom colors and background colors.
</us>
```vue
<template>
<div>
<a-avatar icon="user" />
<a-avatar>
<a-icon slot="icon" type="user" />
</a-avatar>
<a-avatar>U</a-avatar>
<a-avatar>USER</a-avatar>
<a-avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
<a-avatar style="color: #f56a00; backgroundColor: #fde3cf">
U
</a-avatar>
<a-avatar style="backgroundColor:#87d068" icon="user" />
</div>
</template>
```

View File

@ -0,0 +1,11 @@
## API
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| icon | the `Icon` type for an icon avatar, see `Icon` Component | string | - |
| shape | the shape of avatar | `circle` \| `square` | `circle` |
| size | the size of the avatar | number \| string: `large` `small` `default` | `default` |
| src | the address of the image for an image avatar | string | - |
| srcSet | a list of sources to use for different screen resolutions | string | - |
| alt | This attribute defines the alternative text describing the image | string | - |
| loadError | handler when img load error, return false to prevent default fallback behavior | () => boolean | - |

View File

@ -0,0 +1,11 @@
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| icon | 设置头像的图标类型,可设为 Icon 的 `type` 或 VNode | string \| VNode \| slot | - |
| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | `circle` |
| size | 设置头像的大小 | number \| Enum{ 'large', 'small', 'default' } | `default` |
| src | 图片类头像的资源地址 | string | - |
| srcSet | 设置图片类头像响应式资源地址 | string | - |
| alt | 图像无法显示时的替代文本 | string | - |
| loadError | 图片加载失败的事件,返回 false 会关闭组件默认的 fallback 行为 | () => boolean | - |

View File

@ -0,0 +1,20 @@
<cn>
#### 基本
最简单的用法。
</cn>
<us>
#### basic
The most basic usage.
</us>
```vue
<template>
<div>
<a-back-top />
Scroll down to see the bottom-right
<strong style="color: rgba(64, 64, 64, 0.6)"> gray </strong>
button.
</div>
</template>
```

View File

@ -0,0 +1,39 @@
<cn>
#### 自定义样式
可以自定义回到顶部按钮的样式,限制宽高:`40px * 40px`。
</cn>
<us>
#### Custom style
You can customize the style of the button, just note the size limit: no more than `40px * 40px`.
</us>
```vue
<template>
<div id="components-back-top-demo-custom">
<a-back-top>
<div class="ant-back-top-inner">
UP
</div>
</a-back-top>
Scroll down to see the bottom-right
<strong style="color: #1088e9"> blue </strong>
button.
</div>
</template>
<style scoped>
#components-back-top-demo-custom .ant-back-top {
bottom: 100px;
}
#components-back-top-demo-custom .ant-back-top-inner {
height: 40px;
width: 40px;
line-height: 40px;
border-radius: 4px;
background-color: #1088e9;
color: #fff;
text-align: center;
font-size: 20px;
}
</style>
```

View File

@ -0,0 +1,43 @@
<script>
import Basic from './basic';
import Custom from './custom';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
const md = {
cn: `# BackTop 回到顶部
返回页面顶部的操作按钮
## 何时使用
- 当页面内容区域比较长时
- 当用户需要频繁返回顶部查看相关内容时
## 代码演示`,
us: `# BackTop
\`BackTop\` makes it easy to go back to the top of the page.
## When To Use
- When the page content is very long.
- When you need to go back to the top very frequently in order to view the contents.
## Examples
`,
};
export default {
category: 'Components',
type: 'Other',
zhType: '其他',
subtitle: '回到顶部',
title: 'BackTop',
render() {
return (
<div>
<md cn={md.cn} us={md.us} />
<demo-sort>
<Basic />
<Custom />
</demo-sort>
<api>
<CN slot="cn" />
<US />
</api>
</div>
);
},
};
</script>

View File

@ -0,0 +1,16 @@
## API
> The distance to the bottom is set to `50px` by default, which is overridable.
>
> If you decide to use custom styles, please note the size limit: no more than `40px * 40px`.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| target | specifies the scrollable area dom node | () => HTMLElement | () => window | |
| visibilityHeight | the `BackTop` button will not show until the scroll height reaches this value | number | 400 | |
### events
| Events Name | Description | Arguments | Version |
| --- | --- | --- | --- |
| click | a callback function, which can be executed when you click the button | Function | |

View File

@ -0,0 +1,16 @@
## API
> 有默认样式,距离底部 `50px`,可覆盖。
>
> 自定义样式宽高不大于 40px \* 40px。
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| target | 设置需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | Function | () => window | |
| visibilityHeight | 滚动高度达到此参数值才出现 `BackTop` | number | 400 | |
### 事件
| 事件名称 | 说明 | 回调参数 | 版本 |
| -------- | ------------------ | -------- | ---- |
| click | 点击按钮的回调函数 | Function | |

View File

@ -0,0 +1,26 @@
<cn>
#### 基本
简单的徽章展示,当 `count``0` 时,默认不显示,但是可以使用 `showZero` 修改为显示。
</cn>
<us>
#### basic
Simplest Usage. Badge will be hidden when `count` is `0`, but we can use `showZero` to show it.
</us>
```vue
<template>
<div>
<a-badge count="5">
<a href="#" class="head-example" />
</a-badge>
<a-badge count="0" show-zero>
<a href="#" class="head-example" />
</a-badge>
<a-badge>
<a-icon slot="count" type="clock-circle" style="color: #f5222d" />
<a href="#" class="head-example" />
</a-badge>
</div>
</template>
```

View File

@ -0,0 +1,57 @@
<cn>
#### 动态
展示动态变化的效果。
</cn>
<us>
#### Dynamic
The count will be animated as it changes.
</us>
```vue
<template>
<div>
<div>
<a-badge :count="count">
<a href="#" class="head-example" />
</a-badge>
<a-button-group>
<a-button @click="decline">
<a-icon type="minus" />
</a-button>
<a-button @click="increase">
<a-icon type="plus" />
</a-button>
</a-button-group>
</div>
<div style="margin-top: 10px">
<a-badge :dot="show">
<a href="#" class="head-example" />
</a-badge>
<a-switch v-model="show" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
count: 5,
show: true,
};
},
methods: {
decline() {
let count = this.count - 1;
if (count < 0) {
count = 0;
}
this.count = count;
},
increase() {
this.count++;
},
},
};
</script>
```

View File

@ -0,0 +1,60 @@
<cn>
#### 多彩徽标
1.5.0 后新增。我们添加了多种预设色彩的徽标样式,用作不同场景使用。如果预设值不能满足你的需求,可以设置为具体的色值。
</cn>
<us>
#### Colorful Badge
New feature after 3.16.0. We preset a series of colorful Badge styles for use in different situations. You can also set it to a hex color string for custom color.
</us>
```vue
<template>
<div>
<h4 style="margin-bottom: 16px">
Presets:
</h4>
<div>
<div v-for="color in colors" :key="color">
<a-badge :color="color" :text="color" />
</div>
</div>
<h4 style="margin: 16px 0">
Custom:
</h4>
<div>
<a-badge color="#f50" text="#f50" />
<br />
<a-badge color="#2db7f5" text="#2db7f5" />
<br />
<a-badge color="#87d068" text="#87d068" />
<br />
<a-badge color="#108ee9" text="#108ee9" />
</div>
</div>
</template>
<script>
const colors = [
'pink',
'red',
'yellow',
'orange',
'cyan',
'green',
'blue',
'purple',
'geekblue',
'magenta',
'volcano',
'gold',
'lime',
];
export default {
data() {
return {
colors,
};
},
};
</script>
```

View File

@ -0,0 +1,34 @@
<cn>
#### 讨嫌的小红点
没有具体的数字。
</cn>
<us>
#### Red badge
This will simply display a red badge, without a specific count.
If count equals 0, it won't display the dot.
</us>
```vue
<template>
<div id="components-badge-demo-dot">
<a-badge dot>
<a-icon type="notification" />
</a-badge>
<a-badge :count="0" dot>
<a-icon type="notification" />
</a-badge>
<a-badge dot>
<a href="#">Link something</a>
</a-badge>
</div>
</template>
<style scoped>
#components-badge-demo-dot .anticon-notification {
width: 16px;
height: 16px;
line-height: 16px;
font-size: 16px;
}
</style>
```

View File

@ -0,0 +1,74 @@
<script>
import Basic from './basic.md';
import NoWapper from './no-wrapper';
import Dot from './dot';
import Change from './change';
import Overflow from './overflow';
import Status from './status';
import Title from './title';
import Colors from './colors';
import CN from './../index.zh-CN.md';
import US from './../index.en_US.md';
const md = {
cn: `# Badge徽标数
图标右上角的圆形徽标数字
## 何时使用
一般出现在通知图标或头像的右上角用于显示需要处理的消息条数通过醒目视觉形式吸引用户处理
## 代码演示
`,
us: `# Badge
Small numerical value or status descriptor for UI elements.
## When To Use
Badge normally appears in proximity to notifications or user avatars with eye-catching appeal, typically displaying unread messages count.
## Examples
`,
};
export default {
category: 'Components',
subtitle: '徽标数',
type: 'Data Display',
zhType: '数据展示',
title: 'Badge',
render() {
return (
<div id="components-badge-demo">
<md cn={md.cn} us={md.us} />
<demo-sort>
<Basic />
<NoWapper />
<Overflow />
<Dot />
<Status />
<Change />
<Title />
<Colors />
</demo-sort>
<api>
<CN slot="cn" />
<US />
</api>
</div>
);
},
};
</script>
<style>
#components-badge-demo .ant-badge:not(.ant-badge-not-a-wrapper) {
margin-right: 20px;
}
#components-badge-demo .head-example {
width: 42px;
height: 42px;
border-radius: 4px;
background: #eee;
display: inline-block;
vertical-align: middle;
}
#components-badge-demo .ant-badge-not-a-wrapper:not(.ant-badge-status) {
margin-right: 8px;
}
</style>

View File

@ -0,0 +1,19 @@
<cn>
#### 可点击
用 a 标签进行包裹即可。
</cn>
<us>
#### Clickable
The badge can be wrapped with `a` tag to make it linkable.
</us>
```vue
<template>
<a href="#">
<a-badge count="5">
<span class="head-example" />
</a-badge>
</a>
</template>
```

View File

@ -0,0 +1,27 @@
<cn>
#### 独立使用
不包裹任何元素即是独立使用,可自定样式展现。
在右上角的 badge 则限定为红色。
</cn>
<us>
#### Standalone
Used in standalone when children is empty.
</us>
```vue
<template>
<div>
<a-badge count="25" />
<a-badge
count="4"
:number-style="{
backgroundColor: '#fff',
color: '#999',
boxShadow: '0 0 0 1px #d9d9d9 inset',
}"
/>
<a-badge count="109" :number-style="{ backgroundColor: '#52c41a' }" />
</div>
</template>
```

View File

@ -0,0 +1,28 @@
<cn>
#### 封顶数字
超过 `overflowCount` 的会显示为 `${overflowCount}+`,默认的 `overflowCount``99`
</cn>
<us>
#### Overflow Count
`${overflowCount}+` is displayed when count is larger than `overflowCount`. The default value of `overflowCount` is `99`.
</us>
```vue
<template>
<div>
<a-badge :count="99">
<a href="#" class="head-example" />
</a-badge>
<a-badge :count="100">
<a href="#" class="head-example" />
</a-badge>
<a-badge :count="99" :overflow-count="10">
<a href="#" class="head-example" />
</a-badge>
<a-badge :count="1000" :overflow-count="999">
<a href="#" class="head-example" />
</a-badge>
</div>
</template>
```

View File

@ -0,0 +1,31 @@
<cn>
#### 状态点
用于表示状态的小圆点。
</cn>
<us>
#### Status
Standalone badge with status.
</us>
```vue
<template>
<div>
<a-badge status="success" />
<a-badge status="error" />
<a-badge status="default" />
<a-badge status="processing" />
<a-badge status="warning" />
<br />
<a-badge status="success" text="Success" />
<br />
<a-badge status="error" text="Error" />
<br />
<a-badge status="default" text="Default" />
<br />
<a-badge status="processing" text="Processing" />
<br />
<a-badge status="warning" text="warning" />
</div>
</template>
```

View File

@ -0,0 +1,31 @@
<cn>
#### 自定义标题
设置鼠标放在状态点上时显示的文字
</cn>
<us>
#### Title
The badge will display `title` when hovered over, instead of `count`.
</us>
```vue
<template>
<div id="components-badge-demo-title">
<a-badge :count="5" title="Custom hover text">
<a href="#" class="head-example" />
</a-badge>
</div>
</template>
<style scoped>
#components-badge-demo-title .ant-badge:not(.ant-badge-status) {
margin-right: 20px;
}
.head-example {
width: 42px;
height: 42px;
border-radius: 4px;
background: #eee;
display: inline-block;
}
</style>
```

View File

@ -0,0 +1,24 @@
## API
```html
<a-badge :count="5">
<a href="#" class="head-example" />
</a-badge>
```
```html
<a-badge :count="5" />
```
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| color | Customize Badge dot color | string | - | 1.5.0 |
| count | Number to show in badge | number\|string \| slot | | |
| dot | Whether to display a red dot instead of `count` | boolean | `false` | |
| offset | set offset of the badge dot, like [x, y] | [number\|string, number\|string] | - | |
| overflowCount | Max count to show | number | 99 | |
| showZero | Whether to show badge when `count` is zero | boolean | `false` | |
| status | Set Badge as a status dot | `success` \| `processing` \| `default` \| `error` \| `warning` | `''` | |
| text | If `status` is set, `text` sets the display text of the status `dot` | string | `''` | |
| numberStyle | sets the display style of the status `dot` | object | '' | |
| title | Text to show when hovering over the badge | string | `count` | |

View File

@ -0,0 +1,24 @@
## API
```html
<a-badge :count="5">
<a href="#" class="head-example" />
</a-badge>
```
```html
<a-badge :count="5" />
```
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| color | 自定义小圆点的颜色 | string | - | 1.5.0 |
| count | 展示的数字,大于 overflowCount 时显示为 `${overflowCount}+`,为 0 时隐藏 | number \| string \| slot | | |
| dot | 不展示数字,只有一个小红点 | boolean | false | |
| offset | 设置状态点的位置偏移,格式为 [x, y] | [number\|string, number\|string] | - | |
| overflowCount | 展示封顶的数字值 | number | 99 | |
| showZero | 当数值为 0 时,是否展示 Badge | boolean | false | |
| status | 设置 Badge 为状态点 | Enum{ 'success', 'processing, 'default', 'error', 'warning' } | '' | |
| text | 在设置了 `status` 的前提下有效,设置状态点的文本 | string | '' | |
| numberStyle | 设置状态点的样式 | object | '' | |
| title | 设置鼠标放在状态点上时显示的文字 | string | `count` | |

Some files were not shown because too many files have changed in this diff Show More