mirror of
https://gitee.com/WeBank/fes.js.git
synced 2024-12-02 19:58:18 +08:00
commit
a8afd7976c
@ -26,6 +26,9 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@umijs/utils": "3.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@webank/fes": "^2.0.0-alpha.0",
|
||||
"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 { parseStore } from './helper';
|
||||
|
||||
const namespace = 'plugin-vuex';
|
||||
|
||||
@ -9,66 +10,36 @@ export default (api) => {
|
||||
utils: { Mustache }
|
||||
} = api;
|
||||
|
||||
/**
|
||||
* 获取文件夹所有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);
|
||||
}
|
||||
api.describe({
|
||||
key: 'vuex',
|
||||
config: {
|
||||
schema(joi) {
|
||||
return joi.object();
|
||||
},
|
||||
onChange: api.ConfigChangeType.regenerateTmpFiles
|
||||
}
|
||||
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');
|
||||
api.onGenerateFiles(() => {
|
||||
const root = join(paths.absSrcPath, 'stores');
|
||||
const storePaths = getDirFilePaths(root);
|
||||
const store = parseStore(storePaths, join(root, '/'));
|
||||
|
||||
const root = join(paths.absSrcPath, api.config.singular ? 'store' : 'stores');
|
||||
const store = parseStore(root);
|
||||
const vuexConfig = api.config.vuex || {};
|
||||
// 文件写出
|
||||
api.writeTmpFile({
|
||||
path: absRuntimeFilePath,
|
||||
path: absCoreFilePath,
|
||||
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_PLUGINS: store.importPlugins.join('\n'),
|
||||
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']
|
||||
});
|
||||
});
|
||||
|
||||
api.addPluginExports(() => [
|
||||
{
|
||||
specifiers: ['MUTATION_TYPES', 'ACTION_TYPES', 'GETTER_TYPES'],
|
||||
source: absCoreFilePath
|
||||
}
|
||||
]);
|
||||
|
||||
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: '/',
|
||||
access: {
|
||||
roles: {
|
||||
admin: ["/", "/onepiece"]
|
||||
admin: ["/", "/onepiece", '/store']
|
||||
}
|
||||
},
|
||||
mock: {
|
||||
@ -29,6 +29,8 @@ export default {
|
||||
name: 'index'
|
||||
}, {
|
||||
name: 'onepiece'
|
||||
}, {
|
||||
name: 'store'
|
||||
}]
|
||||
},
|
||||
locale: {
|
||||
@ -39,5 +41,8 @@ export default {
|
||||
},
|
||||
enums: {
|
||||
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 roles" :key="item.key">{{item.name}}:{{item.disabled}}</div>
|
||||
<div>{{enumsGet('roles', '2', { dir: 'eName' })}}</div>
|
||||
<h4>Vuex <button @click="increment">click me:{{count}}</button></h4>
|
||||
</div>
|
||||
</template>
|
||||
<config>
|
||||
@ -19,8 +18,7 @@
|
||||
}
|
||||
</config>
|
||||
<script>
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useStore } from 'vuex';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import {
|
||||
access, useAccess, useRouter, useI18n, locale, enums, request
|
||||
} from '@webank/fes';
|
||||
@ -65,8 +63,6 @@ export default {
|
||||
]
|
||||
});
|
||||
console.log(roles);
|
||||
const store = useStore();
|
||||
console.log('store==>', store);
|
||||
onMounted(() => {
|
||||
console.log(router);
|
||||
setTimeout(() => {
|
||||
@ -105,9 +101,7 @@ export default {
|
||||
accessOnepicess,
|
||||
t: localI18n.t,
|
||||
enumsGet: enums.get,
|
||||
roles,
|
||||
count: computed(() => store.state.counter.count),
|
||||
increment: () => store.commit('counter/increment')
|
||||
roles
|
||||
};
|
||||
}
|
||||
};
|
||||
|
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(() => {
|
||||
commit('increment');
|
||||
}, 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