mirror of
https://gitee.com/WeBank/fes.js.git
synced 2024-12-03 20:27:37 +08:00
commit
a8afd7976c
@ -26,6 +26,9 @@
|
|||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@umijs/utils": "3.3.3"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@webank/fes": "^2.0.0-alpha.0",
|
"@webank/fes": "^2.0.0-alpha.0",
|
||||||
"vue": "3.0.5",
|
"vue": "3.0.5",
|
||||||
|
160
packages/fes-plugin-vuex/src/helper.js
Normal file
160
packages/fes-plugin-vuex/src/helper.js
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import { parser } from '@umijs/utils';
|
||||||
|
import { readdirSync, readFileSync, statSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件夹所有JS文件路径
|
||||||
|
* @param {string} dir
|
||||||
|
*/
|
||||||
|
function getDirFilePaths(dir) {
|
||||||
|
const dirs = readdirSync(dir);
|
||||||
|
let pathList = [];
|
||||||
|
for (const name of dirs) {
|
||||||
|
const path = join(dir, name);
|
||||||
|
const info = statSync(path);
|
||||||
|
if (info.isDirectory()) {
|
||||||
|
pathList = pathList.concat(getDirFilePaths(path));
|
||||||
|
} else if (path.endsWith('.js')) {
|
||||||
|
pathList.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pathList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 路径转驼峰
|
||||||
|
* @param {*} path
|
||||||
|
*/
|
||||||
|
function pathToHump(path, root) {
|
||||||
|
return path.replace(root, '')
|
||||||
|
.replace('.js', '')
|
||||||
|
.replace(/(\/|\.|-|_)\S/g, text => text[1].toUpperCase())
|
||||||
|
.replace(/\S/, text => text.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取vuex模块的mutations、actions、getters类型
|
||||||
|
* @param {*} ast
|
||||||
|
* @param {*} name
|
||||||
|
*/
|
||||||
|
function getModelTypes(ast, name, namespace = '') {
|
||||||
|
const types = {
|
||||||
|
mutations: {},
|
||||||
|
actions: {},
|
||||||
|
getters: {}
|
||||||
|
};
|
||||||
|
let namespaced = false;
|
||||||
|
if (ast.type !== 'ObjectExpression') return types;
|
||||||
|
ast.properties.forEach((node) => {
|
||||||
|
if (node.key.name === 'namespaced' && node.value.value) {
|
||||||
|
namespaced = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Object.keys(types).includes(node.key.name)) {
|
||||||
|
let type = types[node.key.name];
|
||||||
|
if (namespaced) {
|
||||||
|
type = types[node.key.name][name];
|
||||||
|
if (!type) {
|
||||||
|
// eslint-disable-next-line no-multi-assign
|
||||||
|
type = types[node.key.name][name] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.value.properties.forEach((prop) => {
|
||||||
|
const key = prop.key && prop.key.name;
|
||||||
|
if (key) {
|
||||||
|
type[key] = `${namespace}${namespaced ? `${name}/` : ''}${key}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (node.key.name === 'modules') {
|
||||||
|
node.value.properties.forEach((prop) => {
|
||||||
|
const subTypes = getModelTypes(prop.value, prop.key.name, `${namespace}${namespaced ? `${name}/` : ''}`);
|
||||||
|
Object.keys(types).forEach((key) => {
|
||||||
|
if (namespaced) {
|
||||||
|
types[key][name] = {
|
||||||
|
...subTypes[key],
|
||||||
|
...types[key][name]
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
types[key] = {
|
||||||
|
...subTypes[key],
|
||||||
|
...types[key]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析模块
|
||||||
|
* @param {*} paths
|
||||||
|
* @param {*} root
|
||||||
|
*/
|
||||||
|
function parseModel(paths = [], root) {
|
||||||
|
const modules = [];
|
||||||
|
const importModules = [];
|
||||||
|
let MUTATION_TYPES = {};
|
||||||
|
let ACTION_TYPES = {};
|
||||||
|
let GETTER_TYPES = {};
|
||||||
|
paths.forEach((path) => {
|
||||||
|
const moduleName = pathToHump(path, root);
|
||||||
|
importModules.push(`import ${moduleName} from '${path}'`);
|
||||||
|
modules.push(moduleName);
|
||||||
|
const content = readFileSync(path).toString('utf-8');
|
||||||
|
let ast = parser.parse(content, {
|
||||||
|
sourceType: 'module',
|
||||||
|
plugins: ['jsx', 'typescript']
|
||||||
|
});
|
||||||
|
ast = ast.program.body.filter(body => body.type === 'ExportDefaultDeclaration')[0];
|
||||||
|
if (ast) {
|
||||||
|
const { mutations, actions, getters } = getModelTypes(ast.declaration, moduleName);
|
||||||
|
MUTATION_TYPES = {
|
||||||
|
...mutations,
|
||||||
|
...MUTATION_TYPES
|
||||||
|
};
|
||||||
|
ACTION_TYPES = {
|
||||||
|
...actions,
|
||||||
|
...ACTION_TYPES
|
||||||
|
};
|
||||||
|
GETTER_TYPES = {
|
||||||
|
...getters,
|
||||||
|
...GETTER_TYPES
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
modules, importModules, MUTATION_TYPES, ACTION_TYPES, GETTER_TYPES
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePlugin(paths = [], root) {
|
||||||
|
const plugins = [];
|
||||||
|
const importPlugins = [];
|
||||||
|
paths.forEach((path) => {
|
||||||
|
const moduleName = pathToHump(path, root);
|
||||||
|
importPlugins.push(`import ${moduleName} from '${path}'`);
|
||||||
|
plugins.push(moduleName);
|
||||||
|
});
|
||||||
|
return { plugins, importPlugins };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseStore(root) {
|
||||||
|
const paths = getDirFilePaths(root);
|
||||||
|
const modelPaths = [];
|
||||||
|
const pluginPaths = [];
|
||||||
|
paths.forEach((path) => {
|
||||||
|
if (path.indexOf('plugin') > -1) {
|
||||||
|
pluginPaths.push(path);
|
||||||
|
} else {
|
||||||
|
modelPaths.push(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...parsePlugin(pluginPaths, root),
|
||||||
|
...parseModel(modelPaths, root)
|
||||||
|
};
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import { readdirSync, readFileSync, statSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
import { parseStore } from './helper';
|
||||||
|
|
||||||
const namespace = 'plugin-vuex';
|
const namespace = 'plugin-vuex';
|
||||||
|
|
||||||
@ -9,66 +10,36 @@ export default (api) => {
|
|||||||
utils: { Mustache }
|
utils: { Mustache }
|
||||||
} = api;
|
} = api;
|
||||||
|
|
||||||
/**
|
api.describe({
|
||||||
* 获取文件夹所有JS文件路径
|
key: 'vuex',
|
||||||
* @param {string} dir
|
config: {
|
||||||
*/
|
schema(joi) {
|
||||||
function getDirFilePaths(dir) {
|
return joi.object();
|
||||||
const dirs = readdirSync(dir);
|
},
|
||||||
let pathList = [];
|
onChange: api.ConfigChangeType.regenerateTmpFiles
|
||||||
for (const name of dirs) {
|
|
||||||
const path = join(dir, name);
|
|
||||||
const info = statSync(path);
|
|
||||||
if (info.isDirectory()) {
|
|
||||||
pathList = pathList.concat(getDirFilePaths(path));
|
|
||||||
} else if (path.endsWith('.js')) {
|
|
||||||
pathList.push(path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return pathList;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析vuex模块及插件文件
|
|
||||||
* @param {Array<string>} pathList 文件路径
|
|
||||||
* @param {string} root
|
|
||||||
*/
|
|
||||||
function parseStore(pathList, root) {
|
|
||||||
const store = {
|
|
||||||
modules: [],
|
|
||||||
plugins: [],
|
|
||||||
importModules: [],
|
|
||||||
importPlugins: []
|
|
||||||
};
|
|
||||||
for (const path of pathList) {
|
|
||||||
const moduleName = path.replace(root, '').replace('.js', '').replace(/(\/|\.|-|_)\S/g, text => text[1].toUpperCase());
|
|
||||||
if (path.indexOf('plugin') > -1) {
|
|
||||||
store.importPlugins.push(`import ${moduleName} from '${path}'`);
|
|
||||||
store.plugins.push(moduleName);
|
|
||||||
} else {
|
|
||||||
store.importModules.push(`import ${moduleName} from '${path}'`);
|
|
||||||
store.modules.push(`${moduleName}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const absCoreFilePath = join(namespace, 'core.js');
|
||||||
const absRuntimeFilePath = join(namespace, 'runtime.js');
|
const absRuntimeFilePath = join(namespace, 'runtime.js');
|
||||||
api.onGenerateFiles(() => {
|
api.onGenerateFiles(() => {
|
||||||
const root = join(paths.absSrcPath, 'stores');
|
const root = join(paths.absSrcPath, api.config.singular ? 'store' : 'stores');
|
||||||
const storePaths = getDirFilePaths(root);
|
const store = parseStore(root);
|
||||||
const store = parseStore(storePaths, join(root, '/'));
|
const vuexConfig = api.config.vuex || {};
|
||||||
|
|
||||||
// 文件写出
|
// 文件写出
|
||||||
api.writeTmpFile({
|
api.writeTmpFile({
|
||||||
path: absRuntimeFilePath,
|
path: absCoreFilePath,
|
||||||
content: Mustache.render(
|
content: Mustache.render(
|
||||||
readFileSync(join(__dirname, 'runtime/runtime.tpl'), 'utf-8'),
|
readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'),
|
||||||
{
|
{
|
||||||
IMPORT_MODULES: store.importModules.join('\n'),
|
IMPORT_MODULES: store.importModules.join('\n'),
|
||||||
IMPORT_PLUGINS: store.importPlugins.join('\n'),
|
IMPORT_PLUGINS: store.importPlugins.join('\n'),
|
||||||
MODULES: `{ ${store.modules.join(', ')} }`,
|
MODULES: `{ ${store.modules.join(', ')} }`,
|
||||||
PLUGINS: `[${store.plugins.join(', ')}]`
|
PLUGINS: `[${store.plugins.join(', ')}]`,
|
||||||
|
MUTATION_TYPES: JSON.stringify(store.MUTATION_TYPES),
|
||||||
|
ACTION_TYPES: JSON.stringify(store.ACTION_TYPES),
|
||||||
|
GETTER_TYPES: JSON.stringify(store.GETTER_TYPES),
|
||||||
|
VUEX_CONFIG: JSON.stringify(vuexConfig)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
@ -79,5 +50,13 @@ export default (api) => {
|
|||||||
ignore: ['.tpl']
|
ignore: ['.tpl']
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
api.addPluginExports(() => [
|
||||||
|
{
|
||||||
|
specifiers: ['MUTATION_TYPES', 'ACTION_TYPES', 'GETTER_TYPES'],
|
||||||
|
source: absCoreFilePath
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
|
api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
|
||||||
};
|
};
|
||||||
|
26
packages/fes-plugin-vuex/src/runtime/core.tpl
Normal file
26
packages/fes-plugin-vuex/src/runtime/core.tpl
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { createStore } from 'vuex';
|
||||||
|
{{{IMPORT_MODULES}}};
|
||||||
|
{{{IMPORT_PLUGINS}}};
|
||||||
|
|
||||||
|
const modules = {{{MODULES}}};
|
||||||
|
const MUTATION_TYPES = {{{MUTATION_TYPES}}};
|
||||||
|
const ACTION_TYPES = {{{ACTION_TYPES}}};
|
||||||
|
const GETTER_TYPES = {{{GETTER_TYPES}}};
|
||||||
|
const conifg = {{{VUEX_CONFIG}}};
|
||||||
|
|
||||||
|
|
||||||
|
const install = function (app) {
|
||||||
|
app.use(createStore({
|
||||||
|
modules: modules,
|
||||||
|
plugins: {{{PLUGINS}}},
|
||||||
|
strict: conifg.strict,
|
||||||
|
devtools: conifg.devtools
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
install,
|
||||||
|
MUTATION_TYPES,
|
||||||
|
ACTION_TYPES,
|
||||||
|
GETTER_TYPES
|
||||||
|
};
|
6
packages/fes-plugin-vuex/src/runtime/runtime.js
Normal file
6
packages/fes-plugin-vuex/src/runtime/runtime.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// eslint-disable-next-line import/extensions
|
||||||
|
import { install } from './core';
|
||||||
|
|
||||||
|
export function onAppCreated({ app }) {
|
||||||
|
install(app);
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
import { createStore } from 'vuex'
|
|
||||||
{{{IMPORT_MODULES}}}
|
|
||||||
{{{IMPORT_PLUGINS}}}
|
|
||||||
|
|
||||||
export function onAppCreated({ app }) {
|
|
||||||
app.use(createStore({
|
|
||||||
modules: {{{MODULES}}},
|
|
||||||
plugins: {{{PLUGINS}}}
|
|
||||||
}))
|
|
||||||
}
|
|
@ -9,7 +9,7 @@ export default {
|
|||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
access: {
|
access: {
|
||||||
roles: {
|
roles: {
|
||||||
admin: ["/", "/onepiece"]
|
admin: ["/", "/onepiece", '/store']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mock: {
|
mock: {
|
||||||
@ -29,6 +29,8 @@ export default {
|
|||||||
name: 'index'
|
name: 'index'
|
||||||
}, {
|
}, {
|
||||||
name: 'onepiece'
|
name: 'onepiece'
|
||||||
|
}, {
|
||||||
|
name: 'store'
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
locale: {
|
locale: {
|
||||||
@ -39,5 +41,8 @@ export default {
|
|||||||
},
|
},
|
||||||
enums: {
|
enums: {
|
||||||
status: [['0', '无效的'], ['1', '有效的']]
|
status: [['0', '无效的'], ['1', '有效的']]
|
||||||
|
},
|
||||||
|
vuex: {
|
||||||
|
strict: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
<div v-for="item in enumsGet('status')" :key="item.key">{{item.value}}:{{item.key}}</div>
|
<div v-for="item in enumsGet('status')" :key="item.key">{{item.value}}:{{item.key}}</div>
|
||||||
<div v-for="item in roles" :key="item.key">{{item.name}}:{{item.disabled}}</div>
|
<div v-for="item in roles" :key="item.key">{{item.name}}:{{item.disabled}}</div>
|
||||||
<div>{{enumsGet('roles', '2', { dir: 'eName' })}}</div>
|
<div>{{enumsGet('roles', '2', { dir: 'eName' })}}</div>
|
||||||
<h4>Vuex <button @click="increment">click me:{{count}}</button></h4>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<config>
|
<config>
|
||||||
@ -19,8 +18,7 @@
|
|||||||
}
|
}
|
||||||
</config>
|
</config>
|
||||||
<script>
|
<script>
|
||||||
import { ref, onMounted, computed } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useStore } from 'vuex';
|
|
||||||
import {
|
import {
|
||||||
access, useAccess, useRouter, useI18n, locale, enums, request
|
access, useAccess, useRouter, useI18n, locale, enums, request
|
||||||
} from '@webank/fes';
|
} from '@webank/fes';
|
||||||
@ -65,8 +63,6 @@ export default {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
console.log(roles);
|
console.log(roles);
|
||||||
const store = useStore();
|
|
||||||
console.log('store==>', store);
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log(router);
|
console.log(router);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -105,9 +101,7 @@ export default {
|
|||||||
accessOnepicess,
|
accessOnepicess,
|
||||||
t: localI18n.t,
|
t: localI18n.t,
|
||||||
enumsGet: enums.get,
|
enumsGet: enums.get,
|
||||||
roles,
|
roles
|
||||||
count: computed(() => store.state.counter.count),
|
|
||||||
increment: () => store.commit('counter/increment')
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
50
packages/fes-template/src/pages/store.vue
Normal file
50
packages/fes-template/src/pages/store.vue
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<div class="haizekuo">
|
||||||
|
<h4>Vuex</h4>
|
||||||
|
<div><button @click="increment">click me:{{doubleCount}}</button></div>
|
||||||
|
<div><button :disabled="disabled" @click="login">async login</button></div>
|
||||||
|
<div><button @click="fooBarIncrement">foo/bar:{{fooBarDoubleCount}}</button></div>
|
||||||
|
<div>{{address}}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<config>
|
||||||
|
{
|
||||||
|
"name": "store",
|
||||||
|
"title": "vuex测试"
|
||||||
|
}
|
||||||
|
</config>
|
||||||
|
<script>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
import { MUTATION_TYPES, GETTER_TYPES, ACTION_TYPES } from '@webank/fes';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const store = useStore();
|
||||||
|
console.log('store==>', store);
|
||||||
|
const disabled = ref(false);
|
||||||
|
return {
|
||||||
|
address: computed(() => store.getters[GETTER_TYPES.user.address]),
|
||||||
|
doubleCount: computed(() => store.getters[GETTER_TYPES.counter.doubleCount]),
|
||||||
|
disabled,
|
||||||
|
increment: () => store.commit(MUTATION_TYPES.counter.increment),
|
||||||
|
login: () => {
|
||||||
|
disabled.value = true;
|
||||||
|
store.dispatch(ACTION_TYPES.user.login).then((res) => {
|
||||||
|
// eslint-disable-next-line no-alert
|
||||||
|
window.alert(res);
|
||||||
|
disabled.value = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fooBarIncrement: () => store.commit(MUTATION_TYPES.fooBar.increment),
|
||||||
|
fooBarDoubleCount: computed(() => store.getters[GETTER_TYPES.fooBar.doubleCount])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.haizekuo {
|
||||||
|
/* background: url('../images/icon.png'); */
|
||||||
|
}
|
||||||
|
</style>
|
23
packages/fes-template/src/stores/foo/bar.js
Normal file
23
packages/fes-template/src/stores/foo/bar.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state: () => ({
|
||||||
|
count: 0
|
||||||
|
}),
|
||||||
|
mutations: {
|
||||||
|
increment(state) {
|
||||||
|
state.count++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
doubleCount(state) {
|
||||||
|
return state.count * 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
asyncIncrement({ commit }) {
|
||||||
|
setTimeout(() => {
|
||||||
|
commit('increment');
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -20,6 +20,35 @@ export default {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
commit('increment');
|
commit('increment');
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
},
|
||||||
|
login() {
|
||||||
|
return new Promise((reslove) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('login');
|
||||||
|
reslove('OK');
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modules: {
|
||||||
|
address: {
|
||||||
|
state: () => ({
|
||||||
|
province: '广东省',
|
||||||
|
city: '深圳市',
|
||||||
|
zone: '南山区'
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
address(state) {
|
||||||
|
return state.province + state.city + state.zone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
posts: {
|
||||||
|
namespaced: true,
|
||||||
|
state: () => ({}),
|
||||||
|
mutations: {
|
||||||
|
doSomething() {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user