{"score":42.24,"report":[{"format":"markup","foundDate":1638413755145,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/BaseLayout.vue","start":{"line":268,"column":9,"position":1782},"end":{"line":298,"column":26,"position":1928},"range":[8710,9627],"fragment":".layout-header {\n padding-left: 24px;\n color: hsla(0,0%,100%,.65);\n background: #001529;\n .layout-menu {\n line-height: 48px;\n }\n .layout-logo {\n display: flex;\n justify-content: flex-start;\n align-items: center;\n min-width: 165px;\n height: 100%;\n overflow: hidden;\n transition: all .3s;\n .logo-img {\n height: 32px;\n width: auto;\n }\n .logo-name {\n overflow: hidden;\n margin: 0 0 0 12px;\n color: #fff;\n font-weight: 600;\n font-size: 18px;\n line-height: 32px;\n }\n }\n }\n }\n .layout-sider-fixed-stuff"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/BaseLayout.vue","start":{"line":233,"column":9,"position":1601},"end":{"line":263,"column":31,"position":1747},"range":[7656,8578],"fragment":".layout-header {\n padding-left: 24px;\n color: hsla(0,0%,100%,.65);\n background: #001529;\n .layout-menu {\n line-height: 48px;\n }\n .layout-logo {\n display: flex;\n justify-content: flex-start;\n align-items: center;\n min-width: 165px;\n height: 100%;\n overflow: hidden;\n transition: all .3s;\n .logo-img {\n height: 32px;\n width: auto;\n }\n .logo-name {\n overflow: hidden;\n margin: 0 0 0 12px;\n color: #fff;\n font-weight: 600;\n font-size: 18px;\n line-height: 32px;\n }\n }\n }\n }\n &.main-layout-navigation-mixin"}},{"format":"markup","foundDate":1638413755150,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/403.vue","start":{"line":2,"column":16,"position":24},"end":{"line":35,"column":2,"position":202},"range":[76,795],"fragment":"\">\n \n 上一页\n \n \n\n\n{\n \"layout\": false\n}\n\n"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/404.vue","start":{"line":2,"column":15,"position":24},"end":{"line":35,"column":2,"position":202},"range":[75,794],"fragment":"\">\n \n 上一页\n \n \n\n\n{\n \"layout\": false\n}\n\n"}},{"format":"javascript","foundDate":1638413755173,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/core/routes/routes.js","start":{"line":75,"column":3,"position":515},"end":{"line":93,"column":17,"position":639},"range":[1825,2184],"fragment":"}\n];\n return routes;\n}\n\nconst ROUTER_BASE = '';\nlet router = null;\nlet history = null;\nexport const createRouter = (routes) => {\n if (router) {\n return router;\n }\n const createHistory = plugin.applyPlugins({\n key: 'modifyCreateHistroy',\n type: ApplyPluginsType.modify,\n args: {\n base: ROUTER_BASE\n },\n initialValue: createWebHistory"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/core/routes/routes.js","start":{"line":26,"column":3,"position":184},"end":{"line":44,"column":21,"position":308},"range":[637,1000],"fragment":"}\n];\n return routes;\n}\n\nconst ROUTER_BASE = '';\nlet router = null;\nlet history = null;\nexport const createRouter = (routes) => {\n if (router) {\n return router;\n }\n const createHistory = plugin.applyPlugins({\n key: 'modifyCreateHistroy',\n type: ApplyPluginsType.modify,\n args: {\n base: ROUTER_BASE\n },\n initialValue: createWebHashHistory"}},{"format":"javascript","foundDate":1638413755174,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/core/routes/routes.js","start":{"line":93,"column":17,"position":640},"end":{"line":133,"column":2,"position":883},"range":[2184,2897],"fragment":",\n });\n history = createHistory(ROUTER_BASE);\n // 修改routes\n plugin.applyPlugins({\n key: 'patchRoutes',\n type: ApplyPluginsType.event,\n args: { routes },\n });\n router = createVueRouter({\n history,\n routes\n });\n\n plugin.applyPlugins({\n key: 'onRouterCreated',\n type: ApplyPluginsType.event,\n args: { router },\n });\n\n return router;\n};\n\nexport const getRouter = ()=>{\n if(!router){\n console.warn(`[preset-build-in] router is null`)\n }\n return router;\n}\n\nexport const getHistory = ()=>{\n if(!history){\n console.warn(`[preset-build-in] history is null`)\n }\n return history;\n}\n\nexport const destroyRouter = ()=>{\n router = null;\n history = null;\n}"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/core/routes/routes.js","start":{"line":44,"column":21,"position":309},"end":{"line":84,"column":2,"position":552},"range":[1000,1713],"fragment":",\n });\n history = createHistory(ROUTER_BASE);\n // 修改routes\n plugin.applyPlugins({\n key: 'patchRoutes',\n type: ApplyPluginsType.event,\n args: { routes },\n });\n router = createVueRouter({\n history,\n routes\n });\n\n plugin.applyPlugins({\n key: 'onRouterCreated',\n type: ApplyPluginsType.event,\n args: { router },\n });\n\n return router;\n};\n\nexport const getRouter = ()=>{\n if(!router){\n console.warn(`[preset-build-in] router is null`)\n }\n return router;\n}\n\nexport const getHistory = ()=>{\n if(!history){\n console.warn(`[preset-build-in] history is null`)\n }\n return history;\n}\n\nexport const destroyRouter = ()=>{\n router = null;\n history = null;\n}"}},{"format":"less","foundDate":1638413755332,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/styles/mixins/hairline.less","start":{"line":1,"column":1,"position":0},"end":{"line":173,"column":2,"position":1037},"range":[0,4232],"fragment":"@import \"../theme\";\n\n.scale-hairline-common(@color, @top, @right, @bottom, @left) {\n content: '';\n position: absolute;\n background-color: @color;\n display: block;\n z-index: 1;\n top: @top;\n right: @right;\n bottom: @bottom;\n left: @left;\n}\n\n.hairline(@direction, @color: @border-color-base) when (@direction ='top') {\n border-top: 1PX solid @color;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n border-top: none;\n position: relative;\n\n &::before {\n .scale-hairline-common(@color, 0, auto, auto, 0);\n width: 100%;\n height: 1PX;\n transform-origin: 50% 50%;\n transform: scaleY(0.5);\n\n @media (min-resolution: 3dppx) {\n transform: scaleY(0.33);\n }\n }\n }\n }\n}\n\n.hairline(@direction, @color: @border-color-base) when (@direction ='right') {\n border-right: 1PX solid @color;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n border-right: none;\n position: relative;\n\n &::after {\n .scale-hairline-common(@color, 0, 0, auto, auto);\n width: 1PX;\n height: 100%;\n background: @color;\n transform-origin: 100% 50%;\n transform: scaleX(0.5);\n\n @media (min-resolution: 3dppx) {\n transform: scaleX(0.33);\n }\n }\n }\n }\n}\n\n.hairline(@direction, @color: @border-color-base) when (@direction ='bottom') {\n border-bottom: 1PX solid @color;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n border-bottom: none;\n position: relative;\n\n &::after {\n .scale-hairline-common(@color, auto, auto, 0, 0);\n width: 100%;\n height: 1PX;\n transform-origin: 50% 100%;\n transform: scaleY(0.5);\n\n @media (min-resolution: 3dppx) {\n transform: scaleY(0.33);\n }\n }\n }\n }\n}\n\n.hairline(@direction, @color: @border-color-base) when (@direction ='left') {\n border-left: 1PX solid @color;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n border-left: none;\n position: relative;\n\n &::before {\n .scale-hairline-common(@color, 0, auto, auto, 0);\n width: 1PX;\n height: 100%;\n transform-origin: 100% 50%;\n transform: scaleX(0.5);\n\n @media (min-resolution: 3dppx) {\n transform: scaleX(0.33);\n }\n }\n }\n }\n}\n\n.hairline(@direction, @color: @border-color-base, @radius: 0) when (@direction ='all') {\n border: 1PX solid @color;\n border-radius: @radius;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n position: relative;\n border: none;\n\n &::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n width: 200%;\n height: 200%;\n border: 1PX solid @color;\n border-radius: @radius * 2;\n transform-origin: 0 0;\n transform: scale(0.5);\n box-sizing: border-box;\n pointer-events: none;\n }\n }\n }\n}\n\n.hairline-remove(@position) when (@position ='left') {\n border-left: 0;\n\n &:before {\n display: none !important;\n }\n}\n\n.hairline-remove(@position) when (@position ='right') {\n border-right: 0;\n\n &:after {\n display: none !important;\n }\n}\n\n.hairline-remove(@position) when (@position ='top') {\n border-top: 0;\n\n &:before {\n display: none !important;\n }\n}\n\n.hairline-remove(@position) when (@position ='bottom') {\n border-bottom: 0;\n\n &:after {\n display: none !important;\n }\n}\n\n.hairline-remove(@position) when (@position ='all') {\n border: 0;\n\n &:before {\n display: none !important;\n }\n}"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/create-fes-app/templates/app/h5/src/styles/mixins/hairline.less","start":{"line":1,"column":1,"position":0},"end":{"line":173,"column":2,"position":1037},"range":[0,4232],"fragment":"@import \"../theme\";\n\n.scale-hairline-common(@color, @top, @right, @bottom, @left) {\n content: '';\n position: absolute;\n background-color: @color;\n display: block;\n z-index: 1;\n top: @top;\n right: @right;\n bottom: @bottom;\n left: @left;\n}\n\n.hairline(@direction, @color: @border-color-base) when (@direction ='top') {\n border-top: 1PX solid @color;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n border-top: none;\n position: relative;\n\n &::before {\n .scale-hairline-common(@color, 0, auto, auto, 0);\n width: 100%;\n height: 1PX;\n transform-origin: 50% 50%;\n transform: scaleY(0.5);\n\n @media (min-resolution: 3dppx) {\n transform: scaleY(0.33);\n }\n }\n }\n }\n}\n\n.hairline(@direction, @color: @border-color-base) when (@direction ='right') {\n border-right: 1PX solid @color;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n border-right: none;\n position: relative;\n\n &::after {\n .scale-hairline-common(@color, 0, 0, auto, auto);\n width: 1PX;\n height: 100%;\n background: @color;\n transform-origin: 100% 50%;\n transform: scaleX(0.5);\n\n @media (min-resolution: 3dppx) {\n transform: scaleX(0.33);\n }\n }\n }\n }\n}\n\n.hairline(@direction, @color: @border-color-base) when (@direction ='bottom') {\n border-bottom: 1PX solid @color;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n border-bottom: none;\n position: relative;\n\n &::after {\n .scale-hairline-common(@color, auto, auto, 0, 0);\n width: 100%;\n height: 1PX;\n transform-origin: 50% 100%;\n transform: scaleY(0.5);\n\n @media (min-resolution: 3dppx) {\n transform: scaleY(0.33);\n }\n }\n }\n }\n}\n\n.hairline(@direction, @color: @border-color-base) when (@direction ='left') {\n border-left: 1PX solid @color;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n border-left: none;\n position: relative;\n\n &::before {\n .scale-hairline-common(@color, 0, auto, auto, 0);\n width: 1PX;\n height: 100%;\n transform-origin: 100% 50%;\n transform: scaleX(0.5);\n\n @media (min-resolution: 3dppx) {\n transform: scaleX(0.33);\n }\n }\n }\n }\n}\n\n.hairline(@direction, @color: @border-color-base, @radius: 0) when (@direction ='all') {\n border: 1PX solid @color;\n border-radius: @radius;\n\n html:not([data-scale]) & {\n @media (min-resolution: 2dppx) {\n position: relative;\n border: none;\n\n &::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n width: 200%;\n height: 200%;\n border: 1PX solid @color;\n border-radius: @radius * 2;\n transform-origin: 0 0;\n transform: scale(0.5);\n box-sizing: border-box;\n pointer-events: none;\n }\n }\n }\n}\n\n.hairline-remove(@position) when (@position ='left') {\n border-left: 0;\n\n &:before {\n display: none !important;\n }\n}\n\n.hairline-remove(@position) when (@position ='right') {\n border-right: 0;\n\n &:after {\n display: none !important;\n }\n}\n\n.hairline-remove(@position) when (@position ='top') {\n border-top: 0;\n\n &:before {\n display: none !important;\n }\n}\n\n.hairline-remove(@position) when (@position ='bottom') {\n border-bottom: 0;\n\n &:after {\n display: none !important;\n }\n}\n\n.hairline-remove(@position) when (@position ='all') {\n border: 0;\n\n &:before {\n display: none !important;\n }\n}"}},{"format":"javascript","foundDate":1638413755409,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/throttle.js","start":{"line":2,"column":1,"position":1},"end":{"line":16,"column":2,"position":140},"range":[1,383],"fragment":"const throttleMap = new Map();\n\nexport default async (ctx, next) => {\n if (ctx.config.throttle) {\n if (throttleMap.get(ctx.key) >= Date.now()) {\n ctx.error = {\n type: 'FREQUENTLY',\n msg: '请求过于频繁'\n };\n return;\n }\n }\n await next();\n throttleMap.set(ctx.key, Date.now() + ctx.config.throttle);\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/throttle.js","start":{"line":2,"column":1,"position":1},"end":{"line":16,"column":2,"position":140},"range":[1,383],"fragment":"const throttleMap = new Map();\n\nexport default async (ctx, next) => {\n if (ctx.config.throttle) {\n if (throttleMap.get(ctx.key) >= Date.now()) {\n ctx.error = {\n type: 'FREQUENTLY',\n msg: '请求过于频繁'\n };\n return;\n }\n }\n await next();\n throttleMap.set(ctx.key, Date.now() + ctx.config.throttle);\n};"}},{"format":"javascript","foundDate":1638413755410,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/setDataField.js","start":{"line":1,"column":1,"position":0},"end":{"line":11,"column":2,"position":134},"range":[0,378],"fragment":"import { isObject } from './helpers';\n\n// FEATURE: 后续支持 a.b.c\nexport default async (ctx, next) => {\n const dataField = ctx.config.dataField ?? ctx.dataField;\n if (!ctx.error && ctx.response && isObject(ctx.response.data) && dataField) {\n ctx.response._rawData = ctx.response.data;\n ctx.response.data = ctx.response.data[dataField];\n }\n await next();\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/setDataField.js","start":{"line":1,"column":1,"position":0},"end":{"line":11,"column":2,"position":134},"range":[0,378],"fragment":"import { isObject } from './helpers';\n\n// FEATURE: 后续支持 a.b.c\nexport default async (ctx, next) => {\n const dataField = ctx.config.dataField ?? ctx.dataField;\n if (!ctx.error && ctx.response && isObject(ctx.response.data) && dataField) {\n ctx.response._rawData = ctx.response.data;\n ctx.response.data = ctx.response.data[dataField];\n }\n await next();\n};"}},{"format":"javascript","foundDate":1638413755411,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/scheduler.js","start":{"line":2,"column":1,"position":1},"end":{"line":33,"column":2,"position":307},"range":[1,952],"fragment":"class Scheduler {\n constructor() {\n this.middlewares = [];\n }\n\n use(fn) {\n if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');\n this.middlewares.push(fn);\n return this;\n }\n\n compose() {\n return (context, next) => {\n let index = -1;\n const dispatch = (i) => {\n if (i <= index) return Promise.reject(new Error('next() called multiple times'));\n index = i;\n let fn = this.middlewares[i];\n if (index === this.middlewares.length) fn = next;\n if (!fn) return Promise.resolve();\n try {\n return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));\n } catch (e) {\n return Promise.reject(e);\n }\n };\n return dispatch(0);\n };\n }\n}\n\nexport default new Scheduler();"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/scheduler.js","start":{"line":2,"column":1,"position":1},"end":{"line":33,"column":2,"position":307},"range":[1,952],"fragment":"class Scheduler {\n constructor() {\n this.middlewares = [];\n }\n\n use(fn) {\n if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');\n this.middlewares.push(fn);\n return this;\n }\n\n compose() {\n return (context, next) => {\n let index = -1;\n const dispatch = (i) => {\n if (i <= index) return Promise.reject(new Error('next() called multiple times'));\n index = i;\n let fn = this.middlewares[i];\n if (index === this.middlewares.length) fn = next;\n if (!fn) return Promise.resolve();\n try {\n return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));\n } catch (e) {\n return Promise.reject(e);\n }\n };\n return dispatch(0);\n };\n }\n}\n\nexport default new Scheduler();"}},{"format":"javascript","foundDate":1638413755412,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/resErrorProcess.js","start":{"line":1,"column":1,"position":0},"end":{"line":17,"column":2,"position":133},"range":[0,387],"fragment":"import { isObject } from './helpers';\n\n// 错误处理等副作用网上提\nexport default async (ctx, next) => {\n const {\n response,\n config\n } = ctx;\n if (!config.closeResDataCheck && response && isObject(response.data)) {\n const code = response.data.code;\n if (code !== '0') {\n ctx.error = response; // code 不为零进入 reject\n }\n }\n\n await next();\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/resErrorProcess.js","start":{"line":1,"column":1,"position":0},"end":{"line":17,"column":2,"position":133},"range":[0,387],"fragment":"import { isObject } from './helpers';\n\n// 错误处理等副作用网上提\nexport default async (ctx, next) => {\n const {\n response,\n config\n } = ctx;\n if (!config.closeResDataCheck && response && isObject(response.data)) {\n const code = response.data.code;\n if (code !== '0') {\n ctx.error = response; // code 不为零进入 reject\n }\n }\n\n await next();\n};"}},{"format":"javascript","foundDate":1638413755413,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/resDataAdaptor.js","start":{"line":1,"column":1,"position":0},"end":{"line":8,"column":2,"position":107},"range":[0,326],"fragment":"import { isFunction, isObject, isString } from './helpers';\n\nexport default async ({ response, responseDataAdaptor }, next) => {\n if (isFunction(responseDataAdaptor) && response && (isObject(response.data) || isString(response.data))) {\n response.data = responseDataAdaptor(response.data);\n }\n await next();\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/resDataAdaptor.js","start":{"line":1,"column":1,"position":0},"end":{"line":8,"column":2,"position":107},"range":[0,326],"fragment":"import { isFunction, isObject, isString } from './helpers';\n\nexport default async ({ response, responseDataAdaptor }, next) => {\n if (isFunction(responseDataAdaptor) && response && (isObject(response.data) || isString(response.data))) {\n response.data = responseDataAdaptor(response.data);\n }\n await next();\n};"}},{"format":"javascript","foundDate":1638413755426,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/request.js","start":{"line":40,"column":2,"position":356},"end":{"line":71,"column":5,"position":568},"range":[1252,1924],"fragment":"(fn)) {\n instance.interceptors[type].use(fn);\n }\n });\n}\n\nfunction addRequestInterceptors(instance, interceptors) {\n addInterceptors(instance, interceptors, 'request');\n}\n\nfunction addResponseInterceptors(instance, interceptors) {\n addInterceptors(instance, interceptors, 'response');\n}\n\nasync function axiosMiddleware(context, next) {\n try {\n context.response = await context.instance.request(context.config);\n } catch (error) {\n context.error = error;\n }\n\n await next();\n}\n\nfunction getRequestInstance() {\n const {\n responseDataAdaptor,\n requestInterceptors = [],\n responseInterceptors = [],\n errorHandler,\n ...otherConfigs\n } = _fes"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/request.js","start":{"line":23,"column":11,"position":209},"end":{"line":53,"column":7,"position":420},"range":[834,1563],"fragment":"(fn)) {\n instance.interceptors[type].use(fn);\n }\n });\n}\n\nfunction addRequestInterceptors(instance, interceptors) {\n addInterceptors(instance, interceptors, 'request');\n}\n\nfunction addResponseInterceptors(instance, interceptors) {\n addInterceptors(instance, interceptors, 'response');\n}\n\nasync function axiosMiddleware(context, next) {\n try {\n context.response = await context.instance.request(context.config);\n } catch (error) {\n context.error = error;\n }\n await next();\n}\n\nfunction getRequestInstance() {\n const {\n responseDataAdaptor,\n requestInterceptors = [],\n responseInterceptors = [],\n errorHandler,\n ...otherConfigs\n } = plugin"}},{"format":"javascript","foundDate":1638413755429,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/request.js","start":{"line":98,"column":8,"position":798},"end":{"line":110,"column":2,"position":881},"range":[2816,3034],"fragment":".compose()\n };\n} // DEPRECATED 废弃,使用 axios baseURL\n\n\nfunction handleApiPathBase(url, options = {}) {\n if (url.startsWith('http')) return url;\n\n if (options.base) {\n return `${options.base}${url}`;\n }\n\n return `"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/request.js","start":{"line":88,"column":10,"position":641},"end":{"line":99,"column":10,"position":722},"range":[2500,2738],"fragment":".compose()\n };\n}\n\n// DEPRECATED 废弃,使用 axios baseURL\nfunction handleApiPathBase(url, options = {}) {\n if (url.startsWith('http')) return url;\n\n if (options.base) {\n return `${options.base}${url}`;\n }\n return `/ras-mas"}},{"format":"javascript","foundDate":1638413755431,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/request.js","start":{"line":117,"column":2,"position":962},"end":{"line":181,"column":6,"position":1453},"range":[3254,4669],"fragment":"(options.method)) {\n options.data = data;\n } else {\n options.params = data;\n }\n\n return options;\n}\n\nlet currentRequestInstance = null;\n\nfunction createContext(userConfig) {\n return { ...currentRequestInstance.context,\n config: { ...currentRequestInstance.context.defaultConfig,\n ...userConfig\n }\n };\n}\n\nfunction getResponseCode(response) {\n if (response) {\n if (response._rawData) return response._rawData.code;\n if (response.data) return response.data.code;\n }\n\n return null;\n}\n\nfunction skipErrorHandlerToObj(skipErrorHandler = []) {\n if (!Array.isArray(skipErrorHandler)) {\n skipErrorHandler = [skipErrorHandler];\n }\n\n return skipErrorHandler.reduce((acc, cur) => {\n acc[cur] = true;\n return acc;\n }, {});\n}\n\nfunction handleRequestError({\n errorHandler = {},\n error,\n response,\n config\n}) {\n // 跳过所有错误类型处理\n if (config.skipErrorHandler === true) return;\n const skipObj = skipErrorHandlerToObj(config.skipErrorHandler);\n const resCode = getResponseCode(response);\n let errorKey = 'default';\n\n if (resCode && errorHandler[resCode]) {\n errorKey = resCode;\n } else if (error.type && errorHandler[error.type]) {\n errorKey = error.type;\n } else if (error.response && errorHandler[error.response.status]) {\n errorKey = error.response.status;\n }\n\n if (!skipObj[errorKey] && errorHandler[errorKey]) {\n return errorHandler[errorKey](error);\n }\n}\n\nconst"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/request.js","start":{"line":105,"column":24,"position":795},"end":{"line":171,"column":7,"position":1288},"range":[2949,4497],"fragment":"(options.method)) {\n options.data = data;\n } else {\n options.params = data;\n }\n return options;\n}\n\nlet currentRequestInstance = null;\n\nfunction createContext(userConfig) {\n return {\n ...currentRequestInstance.context,\n config: {\n ...currentRequestInstance.context.defaultConfig,\n ...userConfig\n }\n };\n}\n\n\nfunction getResponseCode(response) {\n if (response) {\n if (response._rawData) return response._rawData.code;\n if (response.data) return response.data.code;\n }\n return null;\n}\n\nfunction skipErrorHandlerToObj(skipErrorHandler = []) {\n if (!Array.isArray(skipErrorHandler)) {\n skipErrorHandler = [skipErrorHandler];\n }\n\n return skipErrorHandler.reduce((acc, cur) => {\n acc[cur] = true;\n return acc;\n }, {});\n}\n\nfunction handleRequestError({\n errorHandler = {},\n error,\n response,\n config\n}) {\n // 跳过所有错误类型处理\n if (config.skipErrorHandler === true) return;\n\n const skipObj = skipErrorHandlerToObj(config.skipErrorHandler);\n const resCode = getResponseCode(response);\n\n let errorKey = 'default';\n if (resCode && errorHandler[resCode]) {\n errorKey = resCode;\n } else if (error.type && errorHandler[error.type]) {\n errorKey = error.type;\n } else if (error.response && errorHandler[error.response.status]) {\n errorKey = error.response.status;\n }\n\n if (!skipObj[errorKey] && errorHandler[errorKey]) {\n return errorHandler[errorKey](error);\n }\n}\n\nexport"}},{"format":"javascript","foundDate":1638413755432,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/request.js","start":{"line":181,"column":1,"position":1453},"end":{"line":204,"column":8,"position":1662},"range":[4664,5274],"fragment":"const request = (url, data, options = {}) => {\n if (typeof options === 'string') {\n options = {\n method: options\n };\n }\n\n if (!currentRequestInstance) {\n currentRequestInstance = getRequestInstance();\n }\n\n const userConfig = userConfigHandler(url, data, options);\n const context = createContext(userConfig);\n return currentRequestInstance.request(context).then(async () => {\n if (!context.error) {\n return context.config.useResonse ? context.response : context.response.data;\n }\n\n await handleRequestError(context);\n return Promise.reject(context.error);\n });\n};\n\nexports"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/request.js","start":{"line":171,"column":2,"position":1290},"end":{"line":192,"column":9,"position":1497},"range":[4498,5163],"fragment":"const request = (url, data, options = {}) => {\n if (typeof options === 'string') {\n options = {\n method: options\n };\n }\n if (!currentRequestInstance) {\n currentRequestInstance = getRequestInstance();\n }\n const userConfig = userConfigHandler(url, data, options);\n const context = createContext(userConfig);\n\n return currentRequestInstance.request(context).then(async () => {\n if (!context.error) {\n return context.config.useResonse ? context.response : context.response.data;\n }\n await handleRequestError(context);\n return Promise.reject(context.error);\n });\n};\n\nfunction"}},{"format":"javascript","foundDate":1638413755437,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/preventRepeatReq.js","start":{"line":1,"column":1,"position":0},"end":{"line":64,"column":2,"position":546},"range":[0,1644],"fragment":"const requestMap = new Map();\n\nconst mergeRequestMap = new Map();\nconst requestQueue = new Map();\n\nfunction handleCachingStart(ctx) {\n const isRequesting = mergeRequestMap.get(ctx.key);\n if (isRequesting) {\n return new Promise((resolve) => {\n const queue = requestQueue.get(ctx.key) || [];\n requestQueue.set(ctx.key, queue.concat(resolve));\n });\n }\n mergeRequestMap.set(ctx.key, true);\n}\n\nfunction handleRepeatRequest(ctx) {\n const queue = requestQueue.get(ctx.key);\n if (queue && queue.length > 0) {\n queue.forEach((resolve) => {\n if (ctx.error) {\n resolve({\n error: ctx.error\n });\n } else {\n resolve({\n response: ctx.response\n });\n }\n });\n }\n requestQueue.delete(ctx.key);\n mergeRequestMap.delete(ctx.key);\n}\n\nexport default async (ctx, next) => {\n if (ctx.config.mergeRequest) {\n const result = await handleCachingStart(ctx);\n if (result) {\n Object.keys(result).forEach((key) => {\n ctx[key] = result[key];\n });\n return;\n }\n } else {\n if (requestMap.get(ctx.key) && !ctx.config.mergeRequest) {\n ctx.error = {\n type: 'REPEAT',\n msg: '重复请求',\n config: ctx.config\n };\n return;\n }\n requestMap.set(ctx.key, true);\n }\n\n await next();\n\n if (ctx.config.mergeRequest) {\n handleRepeatRequest(ctx);\n } else {\n requestMap.delete(ctx.key);\n }\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/preventRepeatReq.js","start":{"line":1,"column":1,"position":0},"end":{"line":64,"column":2,"position":546},"range":[0,1644],"fragment":"const requestMap = new Map();\n\nconst mergeRequestMap = new Map();\nconst requestQueue = new Map();\n\nfunction handleCachingStart(ctx) {\n const isRequesting = mergeRequestMap.get(ctx.key);\n if (isRequesting) {\n return new Promise((resolve) => {\n const queue = requestQueue.get(ctx.key) || [];\n requestQueue.set(ctx.key, queue.concat(resolve));\n });\n }\n mergeRequestMap.set(ctx.key, true);\n}\n\nfunction handleRepeatRequest(ctx) {\n const queue = requestQueue.get(ctx.key);\n if (queue && queue.length > 0) {\n queue.forEach((resolve) => {\n if (ctx.error) {\n resolve({\n error: ctx.error\n });\n } else {\n resolve({\n response: ctx.response\n });\n }\n });\n }\n requestQueue.delete(ctx.key);\n mergeRequestMap.delete(ctx.key);\n}\n\nexport default async (ctx, next) => {\n if (ctx.config.mergeRequest) {\n const result = await handleCachingStart(ctx);\n if (result) {\n Object.keys(result).forEach((key) => {\n ctx[key] = result[key];\n });\n return;\n }\n } else {\n if (requestMap.get(ctx.key) && !ctx.config.mergeRequest) {\n ctx.error = {\n type: 'REPEAT',\n msg: '重复请求',\n config: ctx.config\n };\n return;\n }\n requestMap.set(ctx.key, true);\n }\n\n await next();\n\n if (ctx.config.mergeRequest) {\n handleRepeatRequest(ctx);\n } else {\n requestMap.delete(ctx.key);\n }\n};"}},{"format":"javascript","foundDate":1638413755439,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/paramsProcess.js","start":{"line":1,"column":1,"position":0},"end":{"line":11,"column":2,"position":97},"range":[0,283],"fragment":"import { checkHttpRequestHasBody, trimObj } from './helpers';\n\nexport default async (ctx, next) => {\n const config = ctx.config;\n if (checkHttpRequestHasBody(config.method)) {\n trimObj(config.data);\n } else {\n trimObj(config.params);\n }\n await next();\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/paramsProcess.js","start":{"line":1,"column":1,"position":0},"end":{"line":11,"column":2,"position":97},"range":[0,283],"fragment":"import { checkHttpRequestHasBody, trimObj } from './helpers';\n\nexport default async (ctx, next) => {\n const config = ctx.config;\n if (checkHttpRequestHasBody(config.method)) {\n trimObj(config.data);\n } else {\n trimObj(config.params);\n }\n await next();\n};"}},{"format":"javascript","foundDate":1638413755442,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/helpers.js","start":{"line":5,"column":1,"position":2},"end":{"line":90,"column":2,"position":596},"range":[41,2028],"fragment":"export function typeOf(obj) {\n const map = {\n '[object Boolean]': 'boolean',\n '[object Number]': 'number',\n '[object String]': 'string',\n '[object Function]': 'function',\n '[object Array]': 'array',\n '[object Date]': 'date',\n '[object RegExp]': 'regExp',\n '[object Undefined]': 'undefined',\n '[object Null]': 'null',\n '[object Object]': 'object',\n '[object URLSearchParams]': 'URLSearchParams'\n };\n return map[Object.prototype.toString.call(obj)];\n}\n\nexport function isFunction(obj) {\n return typeOf(obj) === 'function';\n}\n\nexport function isDate(obj) {\n return typeOf(obj) === 'date';\n}\n\nexport function isString(obj) {\n return typeOf(obj) === 'string';\n}\n\nexport function isArray(obj) {\n return typeOf(obj) === 'array';\n}\n\nexport function isObject(obj) {\n return typeOf(obj) === 'object';\n}\n\nexport function isURLSearchParams(obj) {\n return typeOf(obj) === 'URLSearchParams';\n}\n\n// eslint-disable-next-line\nexport const isUndefined = val => val === undefined;\n\nexport const isDefined = val => val != null;\n\n\nexport function checkHttpRequestHasBody(method) {\n method = method.toUpperCase();\n const HTTP_METHOD = {\n GET: {\n request_body: false\n },\n POST: {\n request_body: true\n },\n PUT: {\n request_body: true\n },\n DELETE: {\n request_body: true\n },\n HEAD: {\n request_body: false\n },\n OPTIONS: {\n request_body: false\n },\n PATCH: {\n request_body: true\n }\n };\n return HTTP_METHOD[method].request_body;\n}\n\nexport function trimObj(obj) {\n if (isObject(obj)) {\n Object.entries(obj).forEach(([key, value]) => {\n if (isString(value)) {\n obj[key] = value.trim();\n } else if (isObject(value)) {\n trimObj(value);\n }\n });\n }\n}"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/helpers.js","start":{"line":5,"column":1,"position":2},"end":{"line":90,"column":2,"position":596},"range":[41,2028],"fragment":"export function typeOf(obj) {\n const map = {\n '[object Boolean]': 'boolean',\n '[object Number]': 'number',\n '[object String]': 'string',\n '[object Function]': 'function',\n '[object Array]': 'array',\n '[object Date]': 'date',\n '[object RegExp]': 'regExp',\n '[object Undefined]': 'undefined',\n '[object Null]': 'null',\n '[object Object]': 'object',\n '[object URLSearchParams]': 'URLSearchParams'\n };\n return map[Object.prototype.toString.call(obj)];\n}\n\nexport function isFunction(obj) {\n return typeOf(obj) === 'function';\n}\n\nexport function isDate(obj) {\n return typeOf(obj) === 'date';\n}\n\nexport function isString(obj) {\n return typeOf(obj) === 'string';\n}\n\nexport function isArray(obj) {\n return typeOf(obj) === 'array';\n}\n\nexport function isObject(obj) {\n return typeOf(obj) === 'object';\n}\n\nexport function isURLSearchParams(obj) {\n return typeOf(obj) === 'URLSearchParams';\n}\n\n// eslint-disable-next-line\nexport const isUndefined = val => val === undefined;\n\nexport const isDefined = val => val != null;\n\n\nexport function checkHttpRequestHasBody(method) {\n method = method.toUpperCase();\n const HTTP_METHOD = {\n GET: {\n request_body: false\n },\n POST: {\n request_body: true\n },\n PUT: {\n request_body: true\n },\n DELETE: {\n request_body: true\n },\n HEAD: {\n request_body: false\n },\n OPTIONS: {\n request_body: false\n },\n PATCH: {\n request_body: true\n }\n };\n return HTTP_METHOD[method].request_body;\n}\n\nexport function trimObj(obj) {\n if (isObject(obj)) {\n Object.entries(obj).forEach(([key, value]) => {\n if (isString(value)) {\n obj[key] = value.trim();\n } else if (isObject(value)) {\n trimObj(value);\n }\n });\n }\n}"}},{"format":"javascript","foundDate":1638413755450,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-request/cacheControl.js","start":{"line":1,"column":1,"position":0},"end":{"line":209,"column":2,"position":1521},"range":[0,5546],"fragment":"import {\n isObject, isString, isURLSearchParams, checkHttpRequestHasBody\n} from './helpers';\n/**\n * 缓存实现的功能\n * 1. 唯一定位一个请求(url, data | params, method)\n * 其中请求参数根据请求方法使用其中一个就够了\n * 一个请求同时包含 data | params 参数的设计本身不合理\n * 不对这种情况进行兼容\n * 2. 控制缓存内容的大小,localStorage 只有5M\n * 3. 控制缓存时间\n * session(存在内存中)\n * expireTime 存在localStoreage 中\n * 4. 成功的、且响应内容为json的请求进行缓存\n */\n\n/**\n * 配置数据\n * type: 'ram' | 'sessionStorage' | 'localStorage'\n * cacheTime: ''\n */\n\n\n/**\n * 缓存数据结构\n * cache: {\n * url: 'url', // 缓存 url\n * data: data, // 数据\n * expire: '' // 缓存时间\n * }\n */\n\n/**\n * 请求参数可以为如下类型\n * - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams\n * - Browser only: FormData, File, Blob\n * 只缓存参数类型为: string、plain object、URLSearchParams 或者无参数的 请求\n */\n\nconst CACHE_KEY_PREFIX = '__FES_REQUEST_CACHE:';\nconst CACHE_TYPE = {\n ram: 'ram',\n session: 'sessionStorage',\n local: 'localStorage'\n};\n\nconst CACHE_DATA_MAP = new Map();\n\nfunction genInnerKey(key, cacheType = 'ram') {\n if (cacheType !== CACHE_TYPE.ram) {\n return `${CACHE_KEY_PREFIX}${key}`;\n }\n return key;\n}\n\nfunction canCache(data) {\n return !data || isObject(data) || isString(data) || Array.isArray(data) || isURLSearchParams(data);\n}\n\nfunction setCacheData({\n key,\n cacheType = 'ram',\n data,\n cacheTime = 1000 * 60 * 3\n}) {\n const _key = genInnerKey(key, cacheType);\n\n const currentCacheData = {\n cacheType,\n data,\n cacheTime,\n expire: Date.now() + cacheTime\n };\n if (cacheType !== CACHE_TYPE.ram) {\n const cacheInstance = window[CACHE_TYPE[cacheType]];\n try {\n cacheInstance.setItem(_key, JSON.stringify(currentCacheData));\n } catch (e) {\n // setItem 出现异常,清理缓存\n for (const item in cacheInstance) {\n if (item.startsWith(CACHE_KEY_PREFIX) && Object.prototype.hasOwnProperty.call(cacheInstance, item)) {\n cacheInstance.removeItem(item);\n }\n }\n }\n } else {\n CACHE_DATA_MAP.set(_key, currentCacheData);\n }\n}\n\nfunction isExpire({ expire, cacheTime }) {\n if (!cacheTime || expire >= Date.now()) {\n return false;\n }\n return true;\n}\n\nfunction getCacheData({ key, cacheType = 'ram' }) {\n const _key = genInnerKey(key, cacheType);\n if (cacheType !== CACHE_TYPE.ram) {\n const cacheInstance = window[CACHE_TYPE[cacheType]];\n const text = cacheInstance.getItem(_key) || null;\n try {\n const currentCacheData = JSON.parse(text);\n if (currentCacheData && !isExpire(currentCacheData)) {\n return currentCacheData.data;\n }\n cacheInstance.removeItem(_key);\n return null;\n } catch (e) {\n cacheInstance.removeItem(_key);\n return null;\n }\n } else {\n const currentCacheData = CACHE_DATA_MAP.get(_key);\n if (currentCacheData && !isExpire(currentCacheData)) {\n return currentCacheData.data;\n }\n CACHE_DATA_MAP.delete(_key);\n return null;\n }\n}\n\n// 存储缓存队列\nconst cacheStartFlag = new Map();\nconst cachingQueue = new Map();\n\n/**\n * 等上一次请求结果\n * 1. 如果上一次请求成功,直接使用上一次的请求结果\n * 2. 如果上一次请求失败,重启本次请求\n */\nfunction handleCachingStart(ctx, config) {\n const _key = genInnerKey(ctx.key, config.cache.cacheType);\n const caching = cacheStartFlag.get(_key);\n if (caching) {\n return new Promise((resolve) => {\n const queue = cachingQueue.get(_key) || [];\n cachingQueue.set(_key, queue.concat(resolve));\n });\n }\n cacheStartFlag.set(_key, true);\n}\n\n// 有请求成功的\nfunction handleCachingQueueSuccess(ctx, config) {\n // 移除首次缓存 flag\n const _key = genInnerKey(ctx.key, config.cache.cacheType);\n const queue = cachingQueue.get(_key);\n if (queue && queue.length > 0) {\n queue.forEach((resolve) => {\n resolve({\n response: ctx.response\n });\n });\n }\n cachingQueue.delete(_key);\n cacheStartFlag.delete(_key);\n}\n\n// 处理请求失败\nfunction handleCachingQueueError(ctx, config) {\n const _key = genInnerKey(ctx.key, config.cache.cacheType);\n const queue = cachingQueue.get(_key);\n if (queue && queue.length > 0) {\n const firstResolve = queue.shift();\n firstResolve();\n cachingQueue.set(_key, queue);\n } else {\n cachingQueue.delete(_key);\n cacheStartFlag.delete(_key);\n }\n}\n\nexport default async (ctx, next) => {\n const { config } = ctx;\n if (config.cache) {\n const cacheData = getCacheData({ key: ctx.key, cacheType: config.cache.cacheType });\n if (cacheData) {\n ctx.response = {\n data: cacheData\n };\n return;\n }\n const result = await handleCachingStart(ctx, config);\n if (result) {\n Object.keys(result).forEach((key) => {\n ctx[key] = result[key];\n });\n return;\n }\n }\n await next();\n\n if (config.cache) {\n const requestdata = checkHttpRequestHasBody(config.method) ? config.data : config.params;\n if (!ctx.error && ctx.response && canCache(requestdata) && canCache(ctx.response.data)) {\n handleCachingQueueSuccess(ctx, config);\n\n setCacheData({\n key: ctx.key,\n data: ctx.response.data,\n ...config.cache\n });\n } else {\n handleCachingQueueError(ctx, config);\n }\n }\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-request/cacheControl.js","start":{"line":1,"column":1,"position":0},"end":{"line":209,"column":2,"position":1521},"range":[0,5546],"fragment":"import {\n isObject, isString, isURLSearchParams, checkHttpRequestHasBody\n} from './helpers';\n/**\n * 缓存实现的功能\n * 1. 唯一定位一个请求(url, data | params, method)\n * 其中请求参数根据请求方法使用其中一个就够了\n * 一个请求同时包含 data | params 参数的设计本身不合理\n * 不对这种情况进行兼容\n * 2. 控制缓存内容的大小,localStorage 只有5M\n * 3. 控制缓存时间\n * session(存在内存中)\n * expireTime 存在localStoreage 中\n * 4. 成功的、且响应内容为json的请求进行缓存\n */\n\n/**\n * 配置数据\n * type: 'ram' | 'sessionStorage' | 'localStorage'\n * cacheTime: ''\n */\n\n\n/**\n * 缓存数据结构\n * cache: {\n * url: 'url', // 缓存 url\n * data: data, // 数据\n * expire: '' // 缓存时间\n * }\n */\n\n/**\n * 请求参数可以为如下类型\n * - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams\n * - Browser only: FormData, File, Blob\n * 只缓存参数类型为: string、plain object、URLSearchParams 或者无参数的 请求\n */\n\nconst CACHE_KEY_PREFIX = '__FES_REQUEST_CACHE:';\nconst CACHE_TYPE = {\n ram: 'ram',\n session: 'sessionStorage',\n local: 'localStorage'\n};\n\nconst CACHE_DATA_MAP = new Map();\n\nfunction genInnerKey(key, cacheType = 'ram') {\n if (cacheType !== CACHE_TYPE.ram) {\n return `${CACHE_KEY_PREFIX}${key}`;\n }\n return key;\n}\n\nfunction canCache(data) {\n return !data || isObject(data) || isString(data) || Array.isArray(data) || isURLSearchParams(data);\n}\n\nfunction setCacheData({\n key,\n cacheType = 'ram',\n data,\n cacheTime = 1000 * 60 * 3\n}) {\n const _key = genInnerKey(key, cacheType);\n\n const currentCacheData = {\n cacheType,\n data,\n cacheTime,\n expire: Date.now() + cacheTime\n };\n if (cacheType !== CACHE_TYPE.ram) {\n const cacheInstance = window[CACHE_TYPE[cacheType]];\n try {\n cacheInstance.setItem(_key, JSON.stringify(currentCacheData));\n } catch (e) {\n // setItem 出现异常,清理缓存\n for (const item in cacheInstance) {\n if (item.startsWith(CACHE_KEY_PREFIX) && Object.prototype.hasOwnProperty.call(cacheInstance, item)) {\n cacheInstance.removeItem(item);\n }\n }\n }\n } else {\n CACHE_DATA_MAP.set(_key, currentCacheData);\n }\n}\n\nfunction isExpire({ expire, cacheTime }) {\n if (!cacheTime || expire >= Date.now()) {\n return false;\n }\n return true;\n}\n\nfunction getCacheData({ key, cacheType = 'ram' }) {\n const _key = genInnerKey(key, cacheType);\n if (cacheType !== CACHE_TYPE.ram) {\n const cacheInstance = window[CACHE_TYPE[cacheType]];\n const text = cacheInstance.getItem(_key) || null;\n try {\n const currentCacheData = JSON.parse(text);\n if (currentCacheData && !isExpire(currentCacheData)) {\n return currentCacheData.data;\n }\n cacheInstance.removeItem(_key);\n return null;\n } catch (e) {\n cacheInstance.removeItem(_key);\n return null;\n }\n } else {\n const currentCacheData = CACHE_DATA_MAP.get(_key);\n if (currentCacheData && !isExpire(currentCacheData)) {\n return currentCacheData.data;\n }\n CACHE_DATA_MAP.delete(_key);\n return null;\n }\n}\n\n// 存储缓存队列\nconst cacheStartFlag = new Map();\nconst cachingQueue = new Map();\n\n/**\n * 等上一次请求结果\n * 1. 如果上一次请求成功,直接使用上一次的请求结果\n * 2. 如果上一次请求失败,重启本次请求\n */\nfunction handleCachingStart(ctx, config) {\n const _key = genInnerKey(ctx.key, config.cache.cacheType);\n const caching = cacheStartFlag.get(_key);\n if (caching) {\n return new Promise((resolve) => {\n const queue = cachingQueue.get(_key) || [];\n cachingQueue.set(_key, queue.concat(resolve));\n });\n }\n cacheStartFlag.set(_key, true);\n}\n\n// 有请求成功的\nfunction handleCachingQueueSuccess(ctx, config) {\n // 移除首次缓存 flag\n const _key = genInnerKey(ctx.key, config.cache.cacheType);\n const queue = cachingQueue.get(_key);\n if (queue && queue.length > 0) {\n queue.forEach((resolve) => {\n resolve({\n response: ctx.response\n });\n });\n }\n cachingQueue.delete(_key);\n cacheStartFlag.delete(_key);\n}\n\n// 处理请求失败\nfunction handleCachingQueueError(ctx, config) {\n const _key = genInnerKey(ctx.key, config.cache.cacheType);\n const queue = cachingQueue.get(_key);\n if (queue && queue.length > 0) {\n const firstResolve = queue.shift();\n firstResolve();\n cachingQueue.set(_key, queue);\n } else {\n cachingQueue.delete(_key);\n cacheStartFlag.delete(_key);\n }\n}\n\nexport default async (ctx, next) => {\n const { config } = ctx;\n if (config.cache) {\n const cacheData = getCacheData({ key: ctx.key, cacheType: config.cache.cacheType });\n if (cacheData) {\n ctx.response = {\n data: cacheData\n };\n return;\n }\n const result = await handleCachingStart(ctx, config);\n if (result) {\n Object.keys(result).forEach((key) => {\n ctx[key] = result[key];\n });\n return;\n }\n }\n await next();\n\n if (config.cache) {\n const requestdata = checkHttpRequestHasBody(config.method) ? config.data : config.params;\n if (!ctx.error && ctx.response && canCache(requestdata) && canCache(ctx.response.data)) {\n handleCachingQueueSuccess(ctx, config);\n\n setCacheData({\n key: ctx.key,\n data: ctx.response.data,\n ...config.cache\n });\n } else {\n handleCachingQueueError(ctx, config);\n }\n }\n};"}},{"format":"javascript","foundDate":1638413755665,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-monaco-editor/src/runtime/theme/default.js","start":{"line":2,"column":1,"position":2},"end":{"line":111,"column":2,"position":578},"range":[29,3519],"fragment":"export default {\n register(monaco) {\n monaco.editor.defineTheme('defaultTheme', {\n base: 'vs',\n inherit: true,\n rules: [\n {\n foreground: 'c41a16',\n token: 'string'\n },\n {\n foreground: '1c00cf',\n token: 'constant.numeric'\n },\n {\n foreground: 'aa0d91',\n token: 'keyword'\n },\n {\n foreground: '000000',\n token: 'keyword.operator'\n },\n {\n foreground: 'aa0d91',\n token: 'constant.language'\n },\n {\n foreground: '990000',\n token: 'support.class.exception'\n },\n {\n foreground: '000000',\n token: 'entity.name.function'\n },\n {\n fontStyle: 'bold underline',\n token: 'entity.name.type'\n },\n {\n fontStyle: 'italic',\n token: 'variable.parameter'\n },\n {\n foreground: '007400',\n token: 'comment'\n },\n {\n foreground: 'ff0000',\n token: 'invalid'\n },\n {\n background: 'e71a1100',\n token: 'invalid.deprecated.trailing-whitespace'\n },\n {\n foreground: '000000',\n background: 'fafafafc',\n token: 'text source'\n },\n {\n foreground: 'aa0d91',\n token: 'meta.tag'\n },\n {\n foreground: 'aa0d91',\n token: 'declaration.tag'\n },\n {\n foreground: '000000',\n fontStyle: 'bold',\n token: 'support'\n },\n {\n foreground: 'aa0d91',\n token: 'storage'\n },\n {\n fontStyle: 'bold underline',\n token: 'entity.name.section'\n },\n {\n foreground: '000000',\n fontStyle: 'bold',\n token: 'entity.name.function.frame'\n },\n {\n foreground: '333333',\n token: 'meta.tag.preprocessor.xml'\n },\n {\n foreground: '994500',\n fontStyle: 'italic',\n token: 'entity.other.attribute-name'\n },\n {\n foreground: '881280',\n token: 'entity.name.tag'\n }\n ],\n colors: {\n 'editor.foreground': '#000000',\n 'editor.background': '#FFFFFF',\n 'editor.selectionBackground': '#BAD6FD',\n 'editor.lineHighlightBackground': '#0000001A',\n 'editorCursor.foreground': '#000000',\n 'editorWhitespace.foreground': '#B3B3B3F4'\n }\n });\n }\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-monaco-editor/theme/default.js","start":{"line":2,"column":1,"position":2},"end":{"line":111,"column":2,"position":578},"range":[29,3519],"fragment":"export default {\n register(monaco) {\n monaco.editor.defineTheme('defaultTheme', {\n base: 'vs',\n inherit: true,\n rules: [\n {\n foreground: 'c41a16',\n token: 'string'\n },\n {\n foreground: '1c00cf',\n token: 'constant.numeric'\n },\n {\n foreground: 'aa0d91',\n token: 'keyword'\n },\n {\n foreground: '000000',\n token: 'keyword.operator'\n },\n {\n foreground: 'aa0d91',\n token: 'constant.language'\n },\n {\n foreground: '990000',\n token: 'support.class.exception'\n },\n {\n foreground: '000000',\n token: 'entity.name.function'\n },\n {\n fontStyle: 'bold underline',\n token: 'entity.name.type'\n },\n {\n fontStyle: 'italic',\n token: 'variable.parameter'\n },\n {\n foreground: '007400',\n token: 'comment'\n },\n {\n foreground: 'ff0000',\n token: 'invalid'\n },\n {\n background: 'e71a1100',\n token: 'invalid.deprecated.trailing-whitespace'\n },\n {\n foreground: '000000',\n background: 'fafafafc',\n token: 'text source'\n },\n {\n foreground: 'aa0d91',\n token: 'meta.tag'\n },\n {\n foreground: 'aa0d91',\n token: 'declaration.tag'\n },\n {\n foreground: '000000',\n fontStyle: 'bold',\n token: 'support'\n },\n {\n foreground: 'aa0d91',\n token: 'storage'\n },\n {\n fontStyle: 'bold underline',\n token: 'entity.name.section'\n },\n {\n foreground: '000000',\n fontStyle: 'bold',\n token: 'entity.name.function.frame'\n },\n {\n foreground: '333333',\n token: 'meta.tag.preprocessor.xml'\n },\n {\n foreground: '994500',\n fontStyle: 'italic',\n token: 'entity.other.attribute-name'\n },\n {\n foreground: '881280',\n token: 'entity.name.tag'\n }\n ],\n colors: {\n 'editor.foreground': '#000000',\n 'editor.background': '#FFFFFF',\n 'editor.selectionBackground': '#BAD6FD',\n 'editor.lineHighlightBackground': '#0000001A',\n 'editorCursor.foreground': '#000000',\n 'editorWhitespace.foreground': '#B3B3B3F4'\n }\n });\n }\n};"}},{"format":"markup","foundDate":1638413755668,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-locale/src/runtime/views/SelectLang.vue","start":{"line":1,"column":1,"position":0},"end":{"line":74,"column":2,"position":446},"range":[0,2018],"fragment":"\n \n \n \n \n \n {{item.icon}}\n {{item.label}}\n \n \n \n \n\n\n\n\n"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-locale/views/SelectLang.vue","start":{"line":1,"column":1,"position":0},"end":{"line":74,"column":2,"position":446},"range":[0,2018],"fragment":"\n \n \n \n \n \n {{item.icon}}\n {{item.label}}\n \n \n \n \n\n\n\n\n"}},{"format":"markup","foundDate":1638413755674,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-layout/src/runtime/views/MultiTabProvider.vue","start":{"line":17,"column":17,"position":77},"end":{"line":50,"column":10,"position":306},"range":[417,1567],"fragment":"\n \n \n \n \n \n \n \n \n \n \n 关闭其他\n \n \n 刷新当前页\n \n \n \n \n \n \n \n \n \n \n \n\n\n"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/MultiTabProvider.vue","start":{"line":121,"column":9,"position":787},"end":{"line":180,"column":2,"position":1090},"range":[4088,5479],"fragment":"};\n const getPageKey = (_route) => {\n const selectedPage = findPage(_route.path);\n if (selectedPage) {\n return selectedPage.key;\n }\n return '';\n };\n const handlerMore = ({ key }) => {\n switch (key) {\n case 'closeOtherPage':\n closeOtherPage();\n break;\n case 'reloadPage':\n reloadPage();\n break;\n default:\n }\n };\n return {\n route,\n pageList,\n getPageKey,\n reloadPage,\n switchPage,\n handlerMore,\n onEdit\n };\n }\n};\n\n"}},{"format":"markup","foundDate":1638413755680,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-layout/src/runtime/views/MenuIcon.vue","start":{"line":1,"column":1,"position":0},"end":{"line":61,"column":2,"position":347},"range":[0,1657],"fragment":"\n"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/MenuIcon.vue","start":{"line":1,"column":1,"position":0},"end":{"line":61,"column":2,"position":347},"range":[0,1657],"fragment":"\n"}},{"format":"markup","foundDate":1638413755682,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-layout/src/runtime/views/Menu.vue","start":{"line":1,"column":1,"position":0},"end":{"line":106,"column":2,"position":667},"range":[0,3634],"fragment":"\n \n \n \n \n \n \n \n \n \n \n \n \n {{item2.title}}\n \n \n \n \n {{item1.title}}\n \n \n \n \n \n \n {{item.title}}\n \n \n \n \n\n\n\n\n"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/Menu.vue","start":{"line":1,"column":1,"position":0},"end":{"line":106,"column":2,"position":667},"range":[0,3634],"fragment":"\n \n \n \n \n \n \n \n \n \n \n \n \n {{item2.title}}\n \n \n \n \n {{item1.title}}\n \n \n \n \n \n \n {{item.title}}\n \n \n \n \n\n\n\n\n"}},{"format":"markup","foundDate":1638413755695,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-layout/src/runtime/views/BaseLayout.vue","start":{"line":1,"column":1,"position":0},"end":{"line":378,"column":2,"position":2314},"range":[0,11515],"fragment":"\n \n \n \n \n \n \n {{title}}\n \n \n \n \n \n \n \n \n \n \n {{title}}\n \n \n \n \n {{title}}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {{footer}}\n \n \n \n \n \n \n\n\n\n\n\n"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/BaseLayout.vue","start":{"line":1,"column":1,"position":0},"end":{"line":378,"column":2,"position":2314},"range":[0,11515],"fragment":"\n \n \n \n \n \n \n {{title}}\n \n \n \n \n \n \n \n \n \n \n {{title}}\n \n \n \n \n {{title}}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {{footer}}\n \n \n \n \n \n \n\n\n\n\n\n"}},{"format":"markup","foundDate":1638413755701,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-layout/src/runtime/views/404.vue","start":{"line":1,"column":1,"position":0},"end":{"line":35,"column":2,"position":202},"range":[0,794],"fragment":"\n \n \n 上一页\n \n \n\n\n{\n \"layout\": false\n}\n\n"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/404.vue","start":{"line":1,"column":1,"position":0},"end":{"line":35,"column":2,"position":202},"range":[0,794],"fragment":"\n \n \n 上一页\n \n \n\n\n{\n \"layout\": false\n}\n\n"}},{"format":"markup","foundDate":1638413755702,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-layout/src/runtime/views/403.vue","start":{"line":1,"column":1,"position":0},"end":{"line":35,"column":2,"position":202},"range":[0,795],"fragment":"\n \n \n 上一页\n \n \n\n\n{\n \"layout\": false\n}\n\n"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/views/403.vue","start":{"line":1,"column":1,"position":0},"end":{"line":35,"column":2,"position":202},"range":[0,794],"fragment":"\n \n \n 上一页\n \n \n\n\n{\n \"layout\": false\n}\n\n {\n if (elm.nodeType === 1) {\n if (elm.nodeName.toLowerCase() === 'script') {\n return false;\n }\n\n for (let i = 0; i < elm.attributes.length; i++) {\n const val = elm.attributes[i].value;\n if (isStr(val) && val.toLowerCase().indexOf('on') === 0) {\n return false;\n }\n }\n\n for (let i = 0; i < elm.childNodes.length; i++) {\n if (!isValid(elm.childNodes[i])) {\n return false;\n }\n }\n }\n return true;\n};\n\nexport const validateContent = (svgContent) => {\n const div = document.createElement('div');\n div.innerHTML = svgContent;\n\n // setup this way to ensure it works on our buddy IE\n for (let i = div.childNodes.length - 1; i >= 0; i--) {\n if (div.childNodes[i].nodeName.toLowerCase() !== 'svg') {\n div.removeChild(div.childNodes[i]);\n }\n }\n\n // must only have 1 root element\n const svgElm = div.firstElementChild;\n if (svgElm && svgElm.nodeName.toLowerCase() === 'svg') {\n // root element must be an svg\n // lets double check we've got valid elements\n // do not allow scripts\n if (isValid(svgElm)) {\n return div.innerHTML;\n }\n }\n return '';\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/helpers/svg.js","start":{"line":1,"column":1,"position":0},"end":{"line":49,"column":2,"position":456},"range":[0,1385],"fragment":"const isStr = function (str) {\n return typeof str === 'string';\n};\n\nexport const isValid = (elm) => {\n if (elm.nodeType === 1) {\n if (elm.nodeName.toLowerCase() === 'script') {\n return false;\n }\n\n for (let i = 0; i < elm.attributes.length; i++) {\n const val = elm.attributes[i].value;\n if (isStr(val) && val.toLowerCase().indexOf('on') === 0) {\n return false;\n }\n }\n\n for (let i = 0; i < elm.childNodes.length; i++) {\n if (!isValid(elm.childNodes[i])) {\n return false;\n }\n }\n }\n return true;\n};\n\nexport const validateContent = (svgContent) => {\n const div = document.createElement('div');\n div.innerHTML = svgContent;\n\n // setup this way to ensure it works on our buddy IE\n for (let i = div.childNodes.length - 1; i >= 0; i--) {\n if (div.childNodes[i].nodeName.toLowerCase() !== 'svg') {\n div.removeChild(div.childNodes[i]);\n }\n }\n\n // must only have 1 root element\n const svgElm = div.firstElementChild;\n if (svgElm && svgElm.nodeName.toLowerCase() === 'svg') {\n // root element must be an svg\n // lets double check we've got valid elements\n // do not allow scripts\n if (isValid(svgElm)) {\n return div.innerHTML;\n }\n }\n return '';\n};"}},{"format":"javascript","foundDate":1638413755705,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-layout/src/runtime/helpers/pluginLocale.js","start":{"line":5,"column":2,"position":33},"end":{"line":33,"column":2,"position":292},"range":[94,816],"fragment":"const transTitle = (name) => {\n const sharedLocale = plugin.getShared('locale');\n if (sharedLocale) {\n const { t } = sharedLocale.useI18n();\n return t(name);\n }\n return name;\n};\n\n\nconst _transform = (arr) => {\n if (Array.isArray(arr)) {\n arr.forEach((item) => {\n if (item.title) {\n item._title = item.title;\n item.title = computed(() => transTitle(item._title));\n }\n if (item.children && item.children.length > 0) {\n _transform(item.children);\n }\n });\n }\n};\n\nexport const transform = (menus) => {\n const originData = unref(menus);\n _transform(originData);\n return originData;\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/helpers/pluginLocale.js","start":{"line":5,"column":1,"position":31},"end":{"line":33,"column":2,"position":290},"range":[87,809],"fragment":"const transTitle = (name) => {\n const sharedLocale = plugin.getShared('locale');\n if (sharedLocale) {\n const { t } = sharedLocale.useI18n();\n return t(name);\n }\n return name;\n};\n\n\nconst _transform = (arr) => {\n if (Array.isArray(arr)) {\n arr.forEach((item) => {\n if (item.title) {\n item._title = item.title;\n item.title = computed(() => transTitle(item._title));\n }\n if (item.children && item.children.length > 0) {\n _transform(item.children);\n }\n });\n }\n};\n\nexport const transform = (menus) => {\n const originData = unref(menus);\n _transform(originData);\n return originData;\n};"}},{"format":"javascript","foundDate":1638413755707,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-layout/src/runtime/helpers/pluginAccess.js","start":{"line":1,"column":1,"position":0},"end":{"line":40,"column":2,"position":368},"range":[0,1112],"fragment":"import { unref, computed } from 'vue';\n// eslint-disable-next-line\nimport { useAccess } from '../../plugin-access/core';\n\nif (!useAccess) {\n throw new Error(\n '[plugin-layout]: pLugin-layout depends on plugin-access,please install plugin-access first!'\n );\n}\n\nexport const hasAccessByMenuItem = (item) => {\n let res;\n if (item.path && (!item.children || item.children.length === 0)) {\n res = useAccess(item.path);\n } else if (item.children && item.children.length > 0) {\n res = computed(() => item.children.some((child) => {\n const rst = hasAccessByMenuItem(child);\n return rst && rst.value;\n }));\n }\n return res;\n};\n\nconst _addAccessTag = (arr) => {\n if (Array.isArray(arr)) {\n arr.forEach((item) => {\n item.access = hasAccessByMenuItem(item);\n if (item.children && item.children.length > 0) {\n _addAccessTag(item.children);\n }\n });\n }\n};\n\nexport const transform = (menus) => {\n const originData = unref(menus);\n _addAccessTag(originData);\n\n return originData;\n};"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template/src/.fes/plugin-layout/helpers/pluginAccess.js","start":{"line":1,"column":1,"position":0},"end":{"line":40,"column":2,"position":368},"range":[0,1112],"fragment":"import { unref, computed } from 'vue';\n// eslint-disable-next-line\nimport { useAccess } from '../../plugin-access/core';\n\nif (!useAccess) {\n throw new Error(\n '[plugin-layout]: pLugin-layout depends on plugin-access,please install plugin-access first!'\n );\n}\n\nexport const hasAccessByMenuItem = (item) => {\n let res;\n if (item.path && (!item.children || item.children.length === 0)) {\n res = useAccess(item.path);\n } else if (item.children && item.children.length > 0) {\n res = computed(() => item.children.some((child) => {\n const rst = hasAccessByMenuItem(child);\n return rst && rst.value;\n }));\n }\n return res;\n};\n\nconst _addAccessTag = (arr) => {\n if (Array.isArray(arr)) {\n arr.forEach((item) => {\n item.access = hasAccessByMenuItem(item);\n if (item.children && item.children.length > 0) {\n _addAccessTag(item.children);\n }\n });\n }\n};\n\nexport const transform = (menus) => {\n const originData = unref(menus);\n _addAccessTag(originData);\n\n return originData;\n};"}},{"format":"less","foundDate":1638413755710,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-icon/src/runtime/Icon/icon.less","start":{"line":1,"column":1,"position":0},"end":{"line":50,"column":2,"position":278},"range":[0,1020],"fragment":".inner-icon {\n display: inline-block;\n color: inherit;\n font-style: normal;\n line-height: 0;\n text-align: center;\n text-transform: none;\n outline: none;\n vertical-align: -0.125em;\n text-rendering: optimizeLegibility;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n\n > * {\n line-height: 1;\n }\n\n svg {\n display: inline-block;\n width: 1em;\n height: 1em;\n fill: currentColor;\n }\n\n &::before {\n display: none; // dont display old icon.\n }\n\n &[tabindex] {\n cursor: pointer;\n }\n &--spin {\n display: inline-block;\n animation: loadingCircle 1s infinite linear;\n }\n\n @-webkit-keyframes loadingCircle {\n 100% {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n }\n @keyframes loadingCircle {\n 100% {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n }\n \n}"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-icon/Icon/icon.less","start":{"line":1,"column":1,"position":0},"end":{"line":50,"column":2,"position":278},"range":[0,1020],"fragment":".inner-icon {\n display: inline-block;\n color: inherit;\n font-style: normal;\n line-height: 0;\n text-align: center;\n text-transform: none;\n outline: none;\n vertical-align: -0.125em;\n text-rendering: optimizeLegibility;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n\n > * {\n line-height: 1;\n }\n\n svg {\n display: inline-block;\n width: 1em;\n height: 1em;\n fill: currentColor;\n }\n\n &::before {\n display: none; // dont display old icon.\n }\n\n &[tabindex] {\n cursor: pointer;\n }\n &--spin {\n display: inline-block;\n animation: loadingCircle 1s infinite linear;\n }\n\n @-webkit-keyframes loadingCircle {\n 100% {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n }\n @keyframes loadingCircle {\n 100% {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n }\n \n}"}},{"format":"markup","foundDate":1638413755712,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-plugin-icon/src/runtime/Icon/Icon.vue","start":{"line":1,"column":1,"position":0},"end":{"line":41,"column":2,"position":278},"range":[0,1225],"fragment":""},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/fes-template-h5/src/.fes/plugin-icon/Icon/Icon.vue","start":{"line":1,"column":1,"position":0},"end":{"line":41,"column":2,"position":278},"range":[0,1225],"fragment":""}},{"format":"json","foundDate":1638413755740,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/create-fes-app/templates/app/h5/tsconfig.json","start":{"line":1,"column":1,"position":0},"end":{"line":37,"column":2,"position":233},"range":[0,893],"fragment":"{\n \"compilerOptions\": {\n \"outDir\": \"build/dist\",\n \"module\": \"esnext\",\n \"target\": \"esnext\",\n \"lib\": [\"esnext\", \"dom\"],\n \"sourceMap\": true,\n \"baseUrl\": \".\",\n \"jsx\": \"preserve\",\n \"allowSyntheticDefaultImports\": true,\n \"moduleResolution\": \"node\",\n \"forceConsistentCasingInFileNames\": true,\n \"noImplicitReturns\": true,\n \"suppressImplicitAnyIndexErrors\": true,\n \"noUnusedLocals\": true,\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"experimentalDecorators\": true,\n \"strict\": true,\n \"paths\": {\n \"@/*\": [\"./src/*\"],\n \"@@/*\": [\"./src/.fes/*\"]\n }\n },\n \"include\": [\n \"src/**/*\",\n \"tests/**/*\",\n \"test/**/*\",\n \"__test__/**/*\",\n \"typings/**/*\",\n \"config/**/*\",\n \".eslintrc.js\",\n \".stylelintrc.js\",\n \".prettierrc.js\"\n ],\n \"exclude\": [\"node_modules\", \"build\", \"dist\", \"scripts\", \"src/.fes/*\", \"webpack\", \"jest\"]\n}"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/create-fes-app/templates/app/pc/tsconfig.json","start":{"line":1,"column":1,"position":0},"end":{"line":37,"column":2,"position":233},"range":[0,893],"fragment":"{\n \"compilerOptions\": {\n \"outDir\": \"build/dist\",\n \"module\": \"esnext\",\n \"target\": \"esnext\",\n \"lib\": [\"esnext\", \"dom\"],\n \"sourceMap\": true,\n \"baseUrl\": \".\",\n \"jsx\": \"preserve\",\n \"allowSyntheticDefaultImports\": true,\n \"moduleResolution\": \"node\",\n \"forceConsistentCasingInFileNames\": true,\n \"noImplicitReturns\": true,\n \"suppressImplicitAnyIndexErrors\": true,\n \"noUnusedLocals\": true,\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"experimentalDecorators\": true,\n \"strict\": true,\n \"paths\": {\n \"@/*\": [\"./src/*\"],\n \"@@/*\": [\"./src/.fes/*\"]\n }\n },\n \"include\": [\n \"src/**/*\",\n \"tests/**/*\",\n \"test/**/*\",\n \"__test__/**/*\",\n \"typings/**/*\",\n \"config/**/*\",\n \".eslintrc.js\",\n \".stylelintrc.js\",\n \".prettierrc.js\"\n ],\n \"exclude\": [\"node_modules\", \"build\", \"dist\", \"scripts\", \"src/.fes/*\", \"webpack\", \"jest\"]\n}"}},{"format":"json","foundDate":1638413755741,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/packages/create-fes-app/templates/app/h5/package.json","start":{"line":8,"column":3,"position":43},"end":{"line":31,"column":27,"position":148},"range":[152,567],"fragment":"},\n \"keywords\": [\n \"管理端\",\n \"fes\",\n \"fast\",\n \"easy\",\n \"strong\"\n ],\n \"files\": [\n \".eslintrc.js\",\n \".gitignore\",\n \".fes.js\",\n \".fes.prod.js\",\n \"mock.js\",\n \"package.json\",\n \"README.md\",\n \"tsconfig.json\",\n \"/src\",\n \"/config\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/WeBankFinTech/fes.js.git\",\n \"directory\": \"packages/fes-template-h5\""},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/packages/create-fes-app/templates/app/pc/package.json","start":{"line":11,"column":3,"position":64},"end":{"line":34,"column":24,"position":169},"range":[255,667],"fragment":"},\n \"keywords\": [\n \"管理端\",\n \"fes\",\n \"fast\",\n \"easy\",\n \"strong\"\n ],\n \"files\": [\n \".eslintrc.js\",\n \".gitignore\",\n \".fes.js\",\n \".fes.prod.js\",\n \"mock.js\",\n \"package.json\",\n \"README.md\",\n \"tsconfig.json\",\n \"/src\",\n \"/config\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/WeBankFinTech/fes.js.git\",\n \"directory\": \"packages/fes-template\""}},{"format":"javascript","foundDate":1638413755768,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/docs/zh/reference/plugin/plugins/qiankun.md","start":{"line":149,"column":2,"position":405},"end":{"line":229,"column":6,"position":642},"range":[3260,4520],"fragment":"l` 参数,用于指定加载子应用什么路由页面。\n\n ```vue\n\n \n\n\n```\n\n## 子应用配置\n\n### 第一步:插件注册\n```js\nexport default {\n qiankun: {\n micro: {},\n }\n};\n```\n\n### 第二步:配置运行时生命周期钩子(可选)\n插件会自动为你创建好 `qiankun` 子应用需要的生命周期钩子,但是如果你想在生命周期期间加一些自定义逻辑,可以在子应用的 `src/app.js` 里导出 `qiankun` 对象,并实现每一个生命周期钩子,其中钩子函数的入参 `props` 由主应用自动注入。\n```js\nexport const qiankun = {\n // 应用加载之前\n async bootstrap(props) {\n console.log('app1 bootstrap', props);\n },\n // 应用 render 之前触发\n async mount(props) {\n console.log('app1 mount', props);\n },\n // 当 props 更新时触发\n async update(props){\n console.log('app1 update', props);\n },\n // 应用卸载之后触发\n async unmount(props) {\n console.log('app1 unmount', props);\n },\n};\n\n```\n\n## 父子应用通讯\n\n有两种方式实现\n\n### 配合 [useModel](./model.md) 使用\n\n确保已经安装了 `@fesjs/plugin-model`:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-model\": \"^2.0.0\"\n },\n}\n```\n\n#### 主应用传递 props\n\n- 如果使用 `MicroApp` 组件模式消费子应用,直接通过 props 传递即可:\n```vue\n\n "},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/docs/zh/reference/plugin/plugins/qiankun.md","start":{"line":124,"column":2,"position":305},"end":{"line":156,"column":7,"position":441},"range":[2666,3403],"fragment":"用加载了,但是页面没有渲染出来的情况。\n:::\n```vue\n\n \n\n\n```\n\n#### 使用 `` 组件的方式\n如果我们的路由使用 `history` 模式,那么在使用乾坤时还算方便,主应用和子应用的路由根据base可以很方便的匹配起来,而且不存在冲突。但是当我们使用 `hash` 模式时,就问题很大,主应用和子应用的路由必须一样才可以匹配上,用起来贼不方便。而且不能在一个页面上同时加载多个子应用,路由存在冲突!这时候,`` 出现了,完美解决上面的问题。\n\n\n`` 相比 `` ,需要多传入 `url` 参数,用于指定加载子应用什么路由页面。\n\n ```vue\n\n \n\n\n```\n\n#### 使用 `` 组件的方式\n如果我们的路由使用 `history` 模式,那么在使用乾坤时还算方便,主应用和子应用的路由根据base可以很方便的匹配起来,而且不存在冲突。但是当我们使用 `hash` 模式时,就问题很大,主应用和子应用的路由必须一样才可以匹配上,用起来贼不方便。而且不能在一个页面上同时加载多个子应用,路由存在冲突!这时候,`` 出现了,完美解决上面的问题。\n\n\n`` 相比 `` ,需要多传入 `url` 参数,用于指定加载子应用什么路由页面。\n\n ```vue\n\n \n\n\n```\n\n## 子应用配置\n\n### 第一步:插件注册\n```js\nexport default {\n qiankun: {\n micro: {},\n }\n};\n```\n\n### 第二步:配置运行时生命周期钩子(可选)\n插件会自动为你创建好 `qiankun` 子应用需要的生命周期钩子,但是如果你想在生命周期期间加一些自定义逻辑,可以在子应用的 `src/app.js` 里导出 `qiankun` 对象,并实现每一个生命周期钩子,其中钩子函数的入参 `props` 由主应用自动注入。\n```js\nexport const qiankun = {\n // 应用加载之前\n async bootstrap(props) {\n console.log('app1 bootstrap', props);\n },\n // 应用 render 之前触发\n async mount(props) {\n console.log('app1 mount', props);\n },\n // 当 props 更新时触发\n async update(props){\n console.log('app1 update', props);\n },\n // 应用卸载之后触发\n async unmount(props) {\n console.log('app1 unmount', props);\n },\n};\n\n```\n\n## 父子应用通讯\n\n有两种方式实现\n\n### 配合 [useModel](./model.md) 使用\n\n确保已经安装了 `@fesjs/plugin-model`:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-model\": \"^2.0.0\"\n },\n}\n```\n\n#### 主应用传递 props\n\n- 如果使用 `MicroApp` 组件模式消费子应用,直接通过 props 传递即可:\n```vue\n\n \n\n\n```\n\n- 如果使用路由绑定式消费子应用,那么约定`src/models/qiankunStateForMicro.js` 的模型数据将作为 `props` 船体给子应用,如:\n```js\nimport { reactive } from 'vue';\n\nexport default () => {\n const state = reactive({ c: 1 });\n return {\n state\n };\n};\n```\n\n#### 子应用消费 props\n\n子应用中会自动生成一个全局名为 `qiankunSta"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/docs/zh/reference/plugin/plugins/qiankun.md","start":{"line":124,"column":2,"position":305},"end":{"line":265,"column":1,"position":773},"range":[2666,5094],"fragment":"用加载了,但是页面没有渲染出来的情况。\n:::\n```vue\n\n \n\n\n```\n\n#### 使用 `` 组件的方式\n如果我们的路由使用 `history` 模式,那么在使用乾坤时还算方便,主应用和子应用的路由根据base可以很方便的匹配起来,而且不存在冲突。但是当我们使用 `hash` 模式时,就问题很大,主应用和子应用的路由必须一样才可以匹配上,用起来贼不方便。而且不能在一个页面上同时加载多个子应用,路由存在冲突!这时候,`` 出现了,完美解决上面的问题。\n\n\n`` 相比 `` ,需要多传入 `url` 参数,用于指定加载子应用什么路由页面。\n\n ```vue\n\n \n\n\n```\n\n## 子应用配置\n\n### 第一步:插件注册\n```js\nexport default {\n qiankun: {\n micro: {},\n }\n};\n```\n\n### 第二步:配置运行时生命周期钩子(可选)\n插件会自动为你创建好 `qiankun` 子应用需要的生命周期钩子,但是如果你想在生命周期期间加一些自定义逻辑,可以在子应用的 `src/app.js` 里导出 `qiankun` 对象,并实现每一个生命周期钩子,其中钩子函数的入参 `props` 由主应用自动注入。\n```js\nexport const qiankun = {\n // 应用加载之前\n async bootstrap(props) {\n console.log('app1 bootstrap', props);\n },\n // 应用 render 之前触发\n async mount(props) {\n console.log('app1 mount', props);\n },\n // 当 props 更新时触发\n async update(props){\n console.log('app1 update', props);\n },\n // 应用卸载之后触发\n async unmount(props) {\n console.log('app1 unmount', props);\n },\n};\n\n```\n\n## 父子应用通讯\n\n有两种方式实现\n\n### 配合 [useModel](./model.md) 使用\n\n确保已经安装了 `@fesjs/plugin-model`:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-model\": \"^2.0.0\"\n },\n}\n```\n\n#### 主应用传递 props\n\n- 如果使用 `MicroApp` 组件模式消费子应用,直接通过 props 传递即可:\n```vue\n\n \n\n\n```\n\n- 如果使用路由绑定式消费子应用,那么约定`src/models/qiankunStateForMicro.js` 的模型数据将作为 `props` 船体给子应用,如:\n```js\nimport { reactive } from 'vue';\n\nexport default () => {\n const state = reactive({ c: 1 });\n return {\n state\n };\n};\n```\n\n#### 子应用消费 props\n\n子应用中会自动生成一个全局名为 `qiankunSta"}},{"format":"markdown","foundDate":1638413756416,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/docs/reference/plugin/plugins/qiankun.md","start":{"line":3,"column":1,"position":2},"end":{"line":271,"column":19,"position":796},"range":[2,5189],"fragment":"@fesjs/plugin-qiankun\n\nFes.js plugin for [qiankun](https://qiankun.umijs.org/),参考[@umijs/plugin-qiankun](https://umijs.org/zh-CN/plugins/plugin-qiankun#MicroApp) 实现,喜欢 React 的同学推荐直接用 Umi。\n\n## 启用方式\n在 `package.json` 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-qiankun\": \"^2.0.0\"\n },\n}\n```\n\n## 介绍\n有一种痛叫接手老项目,技术栈老旧,内容多,还要继续维护~\n\n可能目前迁移、升级老项目最好的解决方案就是微前端。`plugin-qiankun` 是基于 `qiankun` 实现的 Fes.js 微前端解决方案。\n\n## 主应用配置\n\n### 第一步:注册子应用\n```js\nexport default {\n qiankun: {\n main: {\n // 注册子应用信息\n apps: [\n {\n name: 'app1', // 唯一 id\n entry: '//localhost:8001', // html entry\n props: {} // 传递给子应用的数据\n },\n {\n name: 'app2', // 唯一 id\n entry: '//localhost:8002', // html entry\n },\n ],\n },\n },\n};\n```\n\n### 第二步:装载子应用\n\n#### 使用路由绑定的方式\n:::warning\n主应用和子应用需要自行适配路由路径!!!待完善...\n:::\n\n假设我们的系统之前有这样的一些路由:\n```js\nexport default {\n router: {\n routes: [{\n \"path\": \"/\",\n \"component\": () => import('@/src/.fes/plugin-layout/index.js'),\n \"children\": [\n {\n \"path\": \"/onepiece\",\n \"component\": () => import('@/pages/onepiece'),\n \"name\": \"onepiece\",\n \"meta\": {\n \"name\": \"onepiece\",\n \"title\": \"onepiece\"\n }\n }\n ]\n }]\n }\n}\n```\n我们现在想在 `/son` 加载子应用 `app1`,只需要增加这样一些配置即可:\n```js {16-23}\nexport default {\n router: {\n routes: [{\n \"path\": \"/\",\n \"component\": () => import('@/src/.fes/plugin-layout/index.js'),\n \"children\": [\n {\n \"path\": \"/onepiece\",\n \"component\": () => import('@/pages/onepiece'),\n \"name\": \"onepiece\",\n \"meta\": {\n \"name\": \"onepiece\",\n \"title\": \"onepiece\"\n }\n },\n {\n \"path\": \"/son\",\n \"meta\": {\n \"name\": \"son\",\n \"title\": \"子应用\",\n \"microApp\": \"app1\"\n }\n }\n ]\n }]\n }\n}\n```\n当前我们依然提倡约定路由的方式,在`src/pages` 目录新建 `son.vue`:\n```vue\n\n{\n \"name\": \"son\",\n \"title\": \"子应用\",\n \"microApp\": \"app1\"\n}\n\n```\n\n\n#### 使用 `` 组件的方式\n:::tip\n建议使用这种方式来引入不带路由的子应用。 否则请自行关注子应用依赖的路由跟当前浏览器 url 是否能正确匹配上,否则很容易出现子应用加载了,但是页面没有渲染出来的情况。\n:::\n```vue\n\n \n\n\n```\n\n#### 使用 `` 组件的方式\n如果我们的路由使用 `history` 模式,那么在使用乾坤时还算方便,主应用和子应用的路由根据base可以很方便的匹配起来,而且不存在冲突。但是当我们使用 `hash` 模式时,就问题很大,主应用和子应用的路由必须一样才可以匹配上,用起来贼不方便。而且不能在一个页面上同时加载多个子应用,路由存在冲突!这时候,`` 出现了,完美解决上面的问题。\n\n\n`` 相比 `` ,需要多传入 `url` 参数,用于指定加载子应用什么路由页面。\n\n ```vue\n\n \n\n\n```\n\n## 子应用配置\n\n### 第一步:插件注册\n```js\nexport default {\n qiankun: {\n micro: {},\n }\n};\n```\n\n### 第二步:配置运行时生命周期钩子(可选)\n插件会自动为你创建好 `qiankun` 子应用需要的生命周期钩子,但是如果你想在生命周期期间加一些自定义逻辑,可以在子应用的 `src/app.js` 里导出 `qiankun` 对象,并实现每一个生命周期钩子,其中钩子函数的入参 `props` 由主应用自动注入。\n```js\nexport const qiankun = {\n // 应用加载之前\n async bootstrap(props) {\n console.log('app1 bootstrap', props);\n },\n // 应用 render 之前触发\n async mount(props) {\n console.log('app1 mount', props);\n },\n // 当 props 更新时触发\n async update(props){\n console.log('app1 update', props);\n },\n // 应用卸载之后触发\n async unmount(props) {\n console.log('app1 unmount', props);\n },\n};\n\n```\n\n## 父子应用通讯\n\n有两种方式实现\n\n### 配合 [useModel](./model.md) 使用\n\n确保已经安装了 `@fesjs/plugin-model`:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-model\": \"^2.0.0\"\n },\n}\n```\n\n#### 主应用传递 props\n\n- 如果使用 `MicroApp` 组件模式消费子应用,直接通过 props 传递即可:\n```vue\n\n \n\n\n```\n\n- 如果使用路由绑定式消费子应用,那么约定`src/models/qiankunStateForMicro.js` 的模型数据将作为 `props` 船体给子应用,如:\n```js\nimport { reactive } from 'vue';\n\nexport default () => {\n const state = reactive({ c: 1 });\n return {\n state\n };\n};\n```\n\n#### 子应用消费 props\n\n子应用中会自动生成一个全局名为 `qiankunStateFromMain` 的 `model`, 可以在任意组件中获取主应用透传的 `props` 的值。\n\n```vue\n\n```\n\n#### 使用 `` 组件的方式\n如果我们的路由使用 `history` 模式,那么在使用乾坤时还算方便,主应用和子应用的路由根据base可以很方便的匹配起来,而且不存在冲突。但是当我们使用 `hash` 模式时,就问题很大,主应用和子应用的路由必须一样才可以匹配上,用起来贼不方便。而且不能在一个页面上同时加载多个子应用,路由存在冲突!这时候,`` 出现了,完美解决上面的问题。\n\n\n`` 相比 `` ,需要多传入 `url` 参数,用于指定加载子应用什么路由页面。\n\n ```vue\n\n \n\n\n```\n\n## 子应用配置\n\n### 第一步:插件注册\n```js\nexport default {\n qiankun: {\n micro: {},\n }\n};\n```\n\n### 第二步:配置运行时生命周期钩子(可选)\n插件会自动为你创建好 `qiankun` 子应用需要的生命周期钩子,但是如果你想在生命周期期间加一些自定义逻辑,可以在子应用的 `src/app.js` 里导出 `qiankun` 对象,并实现每一个生命周期钩子,其中钩子函数的入参 `props` 由主应用自动注入。\n```js\nexport const qiankun = {\n // 应用加载之前\n async bootstrap(props) {\n console.log('app1 bootstrap', props);\n },\n // 应用 render 之前触发\n async mount(props) {\n console.log('app1 mount', props);\n },\n // 当 props 更新时触发\n async update(props){\n console.log('app1 update', props);\n },\n // 应用卸载之后触发\n async unmount(props) {\n console.log('app1 unmount', props);\n },\n};\n\n```\n\n## 父子应用通讯\n\n有两种方式实现\n\n### 配合 [useModel](./model.md) 使用\n\n确保已经安装了 `@fesjs/plugin-model`:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-model\": \"^2.0.0\"\n },\n}\n```\n\n#### 主应用传递 props\n\n- 如果使用 `MicroApp` 组件模式消费子应用,直接通过 props 传递即可:\n```vue\n\n \n\n\n```\n\n- 如果使用路由绑定式消费子应用,那么约定`src/models/qiankunStateForMicro.js` 的模型数据将作为 `props` 船体给子应用,如:\n```js\nimport { reactive } from 'vue';\n\nexport default () => {\n const state = reactive({ c: 1 });\n return {\n state\n };\n};\n```\n\n#### 子应用消费 props\n\n子应用中会自动生成一个全局名为 `qiankunStateFromMain` 的 `model`, 可以在任意组件中获取主应用透传的 `props` 的值。\n\n```vue\n\n```\n\n\n## API\n\n### useModel\n\n**useModel(name)**\n- **类型**:函数\n \n"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/docs/zh/reference/plugin/plugins/model.md","start":{"line":4,"column":1,"position":3},"end":{"line":66,"column":4,"position":266},"range":[3,962],"fragment":"fesjs/plugin-model\n\n## 启用方式\n在 package.json 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-model\": \"^2.0.0\"\n },\n}\n```\n## 介绍\n一种简易的数据管理方案。我们知道 Vue 的理念是用响应式数据驱动UI更新,提供 `reactive` 、 `ref` 等API把数据变成响应式的。我们使用`Provide / Inject`特性,在应用实例中共享响应式数据。\n\n我们约定`src/models` 目录下的文件为项目定义的 `model` 文件。每个文件需要默认导出一个 `function`。\n\n文件名则对应最终 `model` 的 `name`,你可以通过插件提供的 `API` 来消费 `model` 中的数据。\n\n### Model 文件\n**src/models/useAuthModel.js**\n```js\nimport { reactive } from 'vue'\n\nexport default function useAuthModel() {\n const user = reactive({});\n\n const signin = ()=>{\n // todo\n }\n\n const signout = ()=>{\n // todo\n }\n\n return {\n user,\n signin,\n signout\n }\n}\n```\n\n### 在组件中使用 Model\n```vue\n\n```\n\n\n## API\n\n### useModel\n\n**useModel(name)**\n- **类型**:函数\n \n"}},{"format":"markdown","foundDate":1638413756423,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/docs/reference/plugin/plugins/locale.md","start":{"line":4,"column":1,"position":3},"end":{"line":48,"column":21,"position":138},"range":[3,692],"fragment":"fesjs/plugin-locale\n\n## 介绍\n国际化插件,基于 [Vue I18n](https://github.com/intlify/vue-i18n-next),用于解决 i18n 问题。\n## 启用方式\n在 `package.json` 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-locale\": \"^2.0.0\"\n },\n}\n```\n\n\n## 配置\n\n### 约定式配置\nFes.js 约定如下目录,项目就拥有了 `zh-CN` 与 `en-US` 国际化语言切换:\n```\nsrc\n ├── locales\n │ ├── zh-CN.js\n │ └── en-US.js\n └── pages\n │ └── index.vue\n └── app.js\n```\n多语言文件的命名规范:`-.js`\n\n多语言文件的内容规范:键值组成的字面量,如下:\n```js\n// src/locales/zh-CN.js\nexport default {\n menu: {\n interface: '接口'\n },\n overview: '概述',\n i18n: {\n internationalization: '国际化,基于',\n achieve: '实现。',\n ui: '"},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/docs/zh/reference/plugin/plugins/locale.md","start":{"line":4,"column":1,"position":3},"end":{"line":48,"column":21,"position":138},"range":[3,692],"fragment":"fesjs/plugin-locale\n\n## 介绍\n国际化插件,基于 [Vue I18n](https://github.com/intlify/vue-i18n-next),用于解决 i18n 问题。\n## 启用方式\n在 `package.json` 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-locale\": \"^2.0.0\"\n },\n}\n```\n\n\n## 配置\n\n### 约定式配置\nFes.js 约定如下目录,项目就拥有了 `zh-CN` 与 `en-US` 国际化语言切换:\n```\nsrc\n ├── locales\n │ ├── zh-CN.js\n │ └── en-US.js\n └── pages\n │ └── index.vue\n └── app.js\n```\n多语言文件的命名规范:`-.js`\n\n多语言文件的内容规范:键值组成的字面量,如下:\n```js\n// src/locales/zh-CN.js\nexport default {\n menu: {\n interface: '接口'\n },\n overview: '概述',\n i18n: {\n internationalization: '国际化,基于',\n achieve: '实现。',\n ui: '"}},{"format":"markdown","foundDate":1638413756423,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/docs/reference/plugin/plugins/locale.md","start":{"line":49,"column":1,"position":140},"end":{"line":202,"column":14,"position":814},"range":[693,3288],"fragment":"I组件'\n }\n};\n```\n```js\n// src/locales/zh-CN.js\nexport default {\n menu: {\n interface: 'interface'\n },\n overview: 'Overview',\n i18n: {\n internationalization: 'internationalization,base on',\n achieve: 'to achieve.',\n ui: 'UI components'\n }\n};\n```\n想了解更多语言信息配置、匹配规则,请参考 [Vue I18n](https://vue-i18n.intlify.dev/guide/essentials/syntax.html) 文档。\n\n\n### 编译时配置\n在执行 `fes dev` 或者 `fes build` 时,通过此配置生成运行时的代码,在配置文件`.fes.js` 中配置:\n```js\nexport default {\n locale: {\n }\n}\n```\n默认配置为:\n```js\nexport default {\n locale: {\n locale: 'zh-CN', // default locale\n fallbackLocale: 'zh-CN', // set fallback locale\n baseNavigator: true, // 开启浏览器语言检测\n share: true, // 用户是否需要手动改变语言\n }\n} \n```\n所有配置项如下:\n\n#### locale\n- **类型**:`String`\n \n- **默认值**:`zh-CN`\n\n- **详情**:当前的语言。\n\n#### fallbackLocale\n- **类型**:`String`\n \n- **默认值**:`zh-CN`\n\n- **详情**:兜底的语言,如果当前语言找不到配置,则使用默认语言,需要保证默认语言配置文件存在。\n\n#### baseNavigator\n- **类型**:`Boolean`\n \n- **默认值**:`true`\n\n- **详情**:开启浏览器语言检测。\n\n默认情况下,当前语言环境的识别按照:`localStorage` 中 `fes_locale` 值 > 浏览器检测 > `default` 设置的默认语言 > `zh-CN` 中文。\n\n#### share\n- **类型**:`Boolean`\n \n- **默认值**:`true`\n\n- **详情**:是否共享API,共享语言选择器 `{ SelectLang } `,其他插件可以获取到共享内容。\n \n比如:\n```js\nimport { plugin } from \"@@/core/coreExports\";\nconst localeShared = plugin.getShared(\"locale\");\n```\n\n\n### 运行时配置\n暂无。\n\n## API\n\n### locale\n插件 API 通过 `@fesjs/fes` 导出:\n```js\nimport { locale } from '@fesjs/fes'\n```\n\n#### locale.messages\n- **类型**:`Object`\n \n- **详情**:当前的配置的语言信息。\n\n#### locale.setLocale\n- **类型**:`Function`\n \n- **详情**:设置当前的语言。\n- **参数**:\n - locale,语言的名称,应该是符合 `-` 规范的名称。\n- **返回值**:`null`\n```js\nimport { locale } from '@fesjs/fes';\nlocale.setLocale({ locale: 'en-US' });\n```\n\n#### locale.addLocale\n- **类型**:`Function`\n \n- **详情**:手动添加语言配置。\n- **参数**:\n - locale,语言的名称,符合 `-` 规范的名称。\n - messages, 语言信息。\n- **返回值**:`null`\n```js\nimport { locale } from '@fesjs/fes'\nlocale.addLocale({ locale: 'ja-JP', messages: { test: 'テスト' } });\n```\n\n\n#### locale.getAllLocales\n- **类型**:`Function`\n \n- **详情**:获取当前获得所有国际化文件的列表,默认会在 locales 文件夹下寻找类似 `en-US.js` 文件。\n- **参数**:null\n- **返回值**:`Array`\n```js\nimport { locale } from '@fesjs/fes';\nconsole.log(locale.getAllLocales());\n// [\"en-US\", \"id-ID\", \"ja-JP\", \"pt-BR\", \"zh-CN\", \"zh-TW\"]\n```\n\n\n### useI18n\nComposition API, 只能在 `setup` 函数中使用,更多细节参考 [Vue I18n](https://vue-i18n.intlify.dev/api/composition.html#usei18n)。\n\b举个 🌰:\n```vue\n\n \n {{ t('language') }}\n \n message: {{ t('hello') }}\n\n\n\n\n```\n## API\n### get\n* `get(name: string)` 获取指定名字的枚举\n\n* `get(name: string, key: string)` 获取指定名字及键枚举默认值\n\n* `get(name: string, opt: {extend: Array})` 获取指定名字的自定义格式枚举,[查看extend配置](#extend配置)\n\n* `get(name: string, key: string, opt: {dir: string})` 获取指定名字及键枚举[dir规则](#dir规则)的值\n\n```js\nget('status')\nget('status', '1')\nget('status', {\n extend: [\n {\n key: 'name',\n dir: 'value',\n },\n {\n key: 'disabled',\n transfer: item => item === '0'\n }\n ]\n})\nget('status', '1', {dir: 'value'})\n```\n\n### push\n动态添加枚举,重复添加会覆盖\n* `push(name: string, _enum: Array)`\n* `push(name: string, _enum: Array, opt?: Object)`\n * opt.keyName 指定key的取值属性,默认是key\n * opt.valueName 指定value的取值属性\n\n枚举项为数组,枚举项的[0]解析为key,枚举项的[1]解析为value\n\n枚举项为对象时,根据opt配置keyName、valueName取枚举项属性值分别作为key和value,`如果valueName未设置则value就是枚举项`\n\n### remove\n* remove(name: string)\n\n移除指定的枚举\n### concat\n基于现有的枚举,连接上新的枚举后返回新的枚举\n* `concat(name: string, _enum: Array, opt?: Object))`\n * opt.keyName 指定key的取值属性,默认是key\n * opt.valueName 指定value的取值属性\n * opt.before 是否添加在现有的之前,默认是false\n * opt.extend:返回的枚举[extend配置](#extend配置)\n\n### convert\n将传入的枚举格式转换为{key, value}的形式\n* `convert(name: string, _enum: Array, opt?: Object))`\n * opt.keyName 指定key的取值属性,默认是key\n * opt.valueName 指定value的取值属性\n\n### extend配置\n扩展枚举项属性的配置\n* `extend: Array`\n * `key` 指定扩展的属性名\n * `dir` 指定该属性的取值路径\n * `transfer(item: {key: any, value: any})` 转换函数,参数未枚举项,返回就是该属性的值\n::: tip\n同时设置[dir](#dir规则)和transfer,transfer优先\n:::\n\n```js\nget('status', {\n extend: [\n {\n key: 'name',\n dir: 'value',\n },\n {\n key: 'disabled',\n transfer: item => item.key === '0'\n }\n ]\n})\n```\n\n\n### dir规则\ndir是指定枚举项value的取值方式,规则如下:\n* 对象属性 `A`、`A.B`\n* 数组 `[0]`、`[0][1]`\n* 混合 `A[0]`、`[0].A`、`A[0].B`\n\n```js\n// 假如枚举项value的结构如下\nconst user = {\n age: 18,\n name: 'aring',\n role: [\n {\n id: 1,\n name: '管理员'\n },\n {\n id: 2,\n name: '业务操作员'\n }\n ]\n}\n// 那么规则解析是:\ndir "},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/docs/zh/reference/plugin/plugins/enums.md","start":{"line":3,"column":1,"position":2},"end":{"line":226,"column":4,"position":713},"range":[2,4515],"fragment":"@fesjs/plugin-enums\n## 介绍\n日常业务开发中,有很多场景会使用到枚举值,比如select-options、table-column。\n\n该插件提供统一的枚举存取及丰富的函数来处理枚举。\n## 启用方式\n在 `package.json` 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-enums\": \"^2.0.0\"\n }\n}\n```\n\n## 配置\n\n### 静态配置\n在 `.fes.js` 中配置:\n```js\n// 配置格式:[[key, value], ...]\nexport default {\n enums: {\n status: [['0', '无效的'], ['1', '有效的']]\n }\n}\n```\n\n### 动态配置\n在业务代码中\n```js\nimport { enums } from '@fesjs/fes';\n// 动态添加\nenums.push('status', [['0', '无效的'], ['1', '有效的']]\nenums.get('status', '1') // 有效的\n```\n\n## 场景使用\n- 动态添加的枚举项支持数组和对象\n\n- 枚举项为对象时,可以指定keyName和valueName属性名\n\n- 导出枚举值,可指定取值的路径\n\n- 导出枚举可扩展属性\n```vue\n\n \n \n \n {{item.value}}:{{item.key}}\n \n \n \n {{item.name}}:{{item.disabled}}\n \n \n {{enumsGet('roles', '2', { dir: 'eName' })}}\n \n\n\n\n```\n## API\n### get\n* `get(name: string)` 获取指定名字的枚举\n\n* `get(name: string, key: string)` 获取指定名字及键枚举默认值\n\n* `get(name: string, opt: {extend: Array})` 获取指定名字的自定义格式枚举,[查看extend配置](#extend配置)\n\n* `get(name: string, key: string, opt: {dir: string})` 获取指定名字及键枚举[dir规则](#dir规则)的值\n\n```js\nget('status')\nget('status', '1')\nget('status', {\n extend: [\n {\n key: 'name',\n dir: 'value',\n },\n {\n key: 'disabled',\n transfer: item => item === '0'\n }\n ]\n})\nget('status', '1', {dir: 'value'})\n```\n\n### push\n动态添加枚举,重复添加会覆盖\n* `push(name: string, _enum: Array)`\n* `push(name: string, _enum: Array, opt?: Object)`\n * opt.keyName 指定key的取值属性,默认是key\n * opt.valueName 指定value的取值属性\n\n枚举项为数组,枚举项的[0]解析为key,枚举项的[1]解析为value\n\n枚举项为对象时,根据opt配置keyName、valueName取枚举项属性值分别作为key和value,`如果valueName未设置则value就是枚举项`\n\n### remove\n* remove(name: string)\n\n移除指定的枚举\n### concat\n基于现有的枚举,连接上新的枚举后返回新的枚举\n* `concat(name: string, _enum: Array, opt?: Object))`\n * opt.keyName 指定key的取值属性,默认是key\n * opt.valueName 指定value的取值属性\n * opt.before 是否添加在现有的之前,默认是false\n * opt.extend:返回的枚举[extend配置](#extend配置)\n\n### convert\n将传入的枚举格式转换为{key, value}的形式\n* `convert(name: string, _enum: Array, opt?: Object))`\n * opt.keyName 指定key的取值属性,默认是key\n * opt.valueName 指定value的取值属性\n\n### extend配置\n扩展枚举项属性的配置\n* `extend: Array`\n * `key` 指定扩展的属性名\n * `dir` 指定该属性的取值路径\n * `transfer(item: {key: any, value: any})` 转换函数,参数未枚举项,返回就是该属性的值\n::: tip\n同时设置[dir](#dir规则)和transfer,transfer优先\n:::\n\n```js\nget('status', {\n extend: [\n {\n key: 'name',\n dir: 'value',\n },\n {\n key: 'disabled',\n transfer: item => item.key === '0'\n }\n ]\n})\n```\n\n\n### dir规则\ndir是指定枚举项value的取值方式,规则如下:\n* 对象属性 `A`、`A.B`\n* 数组 `[0]`、`[0][1]`\n* 混合 `A[0]`、`[0].A`、`A[0].B`\n\n```js\n// 假如枚举项value的结构如下\nconst user = {\n age: 18,\n name: 'aring',\n role: [\n {\n id: 1,\n name: '管理员'\n },\n {\n id: 2,\n name: '业务操作员'\n }\n ]\n}\n// 那么规则解析是:\ndir "}},{"format":"javascript","foundDate":1638413756455,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/docs/reference/plugin/plugins/editor.md","start":{"line":87,"column":2,"position":268},"end":{"line":100,"column":1,"position":318},"range":[2407,2610],"fragment":"ditor \n v-model=\"json\"\n language=\"json\"\n height=\"400px\"\n check>\n \n\n\n```\n\n#### props\n| 属性 | 说明 | 类型 | 默认值 |\n| ------------- | ------------- | ------------- | ------------- |\n| theme | 编辑器的主题,使用其他主题需要先使用`monaco.editor.defineTheme`定义主题 | string | `defaultTheme` |\n| language | 编辑器的语言 | string | - |\n| height | 编辑器的高度 | string | `100%` |\n| width | 编辑器的宽度 | string | `100%` |\n| modelValue(v-model) | 编辑器的代码 | string | - |\n| readOnly | 是否只读 | boolean | `false` |\n| options | 编辑器的配置对象 | object | `{}` |\n| check | 是否检查代码,如果检查不通过则不更新数据,目前只支持`json` | boolean | `false` |\n\n#### events\n\n| 事件名称 | 说明 | 回调参数 |\n| "},"duplicationB":{"sourceId":"/Users/qlin/code/fes.js/docs/zh/reference/plugin/plugins/editor.md","start":{"line":5,"column":1,"position":4},"end":{"line":120,"column":2,"position":578},"range":[4,3290],"fragment":"esjs/plugin-monaco-editor\n\n\n## 介绍\n我们会遇到需要编辑代码的场景,比如编辑`json`、`javascript`、`python`等等,[Monaco Editor](https://github.com/Microsoft/monaco-editor) 是\b一个好用而且强大的的代码编辑器库,引入`Monaco Editor`有一定的成本,插件实现了胶水代码,提供轻松引入的能力。目前内置的 `Monaco Editor` 版本是 `1.9.1`。\n\n## 启用方式\n在 `package.json` 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-monaco-editor\": \"^2.0.0\"\n },\n}\n```\n\n## 配置\n\n### 编译时配置\n在执行 `fes dev` 或者 `fes build` 时,通过此配置生成运行时的代码,在配置文件`.fes.js` 中配置:\n```js\nexport default {\n monacoEditor: {\n languages: ['javascript', 'typescript', 'html', 'json']\n }\n}\n```\n我们通过 `monaco-editor-webpack-plugin` 集成 `Monaco Editor` 的 `ESM`版本,所以编辑时其实就是 `monaco-editor-webpack-plugin` 的配置,具体配置项参考[文档](https://github.com/Microsoft/monaco-editor-webpack-plugin)。\n\n\n#### filename\n- **类型**:自定义worker脚本名称\n \n- **默认值**:`'[name].worker.js'`\n\n#### publicPath\n- **类型**:自定义worker脚本的路径\n \n- **默认值**:`''`\n\n#### languages\n- **类型**:需要支持的语言类型\n \n- **默认值**:`['abap', 'apex', 'azcli', 'bat', 'bicep', 'cameligo', 'clojure', 'coffee', 'cpp', 'csharp', 'csp', 'css', 'dart', 'dockerfile', 'ecl', 'elixir', 'fsharp', 'go', 'graphql', 'handlebars', 'hcl', 'html', 'ini', 'java', 'javascript', 'json', 'julia', 'kotlin', 'less', 'lexon', 'liquid', 'lua', 'm3', 'markdown', 'mips', 'msdax', 'mysql', 'objective-c', 'pascal', 'pascaligo', 'perl', 'pgsql', 'php', 'postiats', 'powerquery', 'powershell', 'pug', 'python', 'qsharp', 'r', 'razor', 'redis', 'redshift', 'restructuredtext', 'ruby', 'rust', 'sb', 'scala', 'scheme', 'scss', 'shell', 'solidity', 'sophia', 'sparql', 'sql', 'st', 'swift', 'systemverilog', 'tcl', 'twig', 'typescript', 'vb', 'xml', 'yaml']`\n\n- **详情**:默认是全部,但是编译后包体积会非常大,建议用到什么语言则配置什么语言。特别某些语言依赖其他语言,例如`javascript`依赖`typescript`,需要使用`javascript`时需要配置为:\n```js\nexport default {\n monacoEditor: {\n languages: ['javascript', 'typescript']\n }\n}\n```\n\n## API\n\n### monaco\n编辑器的全局对象,提供扩展语言,自定义主题等等API,具体用法请查看[monaco](https://microsoft.github.io/monaco-editor/)官方文档。\n```js\nimport { monaco } from '@fesjs/fes';\n\nmonaco.editor.defineTheme('myCoolTheme', {\n\tbase: 'vs',\n\tinherit: false,\n\trules: [\n\t\t{ token: 'custom-info', foreground: '808080' },\n\t\t{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },\n\t\t{ token: 'custom-notice', foreground: 'FFA500' },\n\t\t{ token: 'custom-date', foreground: '008800' },\n\t]\n});\n\n```\n\n### 组件 MonacoEditor\n\n```vue\n\n \n \n\n\n```\n\n#### props\n| 属性 | 说明 | 类型 | 默认值 |\n| ------------- | ------------- | ------------- | ------------- |\n| theme | 编辑器的主题,使用其他主题需要先使用`monaco.editor.defineTheme`定义主题 | string | `defaultTheme` |\n| language | 编辑器的语言 | string | - |\n| height | 编辑器的高度 | string | `100%` |\n| width | 编辑器的宽度 | string | `100%` |\n| modelValue(v-model) | 编辑器的代码 | string | - |\n| readOnly | 是否只读 | boolean | `false` |\n| options | 编辑器的配置对象 | object | `{}` |\n| check | 是否检查代码,如果检查不通过则不更新数据,目前只支持`json` | boolean | `false` |\n\n#### events\n\n| 事件名称 | 说明 | 回调参数 |\n| "}},{"format":"javascript","foundDate":1638413756459,"duplicationA":{"sourceId":"/Users/qlin/code/fes.js/docs/reference/plugin/plugins/access.md","start":{"line":18,"column":2,"position":76},"end":{"line":280,"column":1,"position":1042},"range":[382,4871],"fragment":"cess2 \n\n\n```\n\n\n### 匹配规则\n\n#### 全等匹配\n资源的匹配规则默认是使用全等匹配,比如页面 `pages/a.vue` 对应路由 `path` 是 `/a`,则 `/a` 就是页面的资源ID。如果我们设置:\n```js\naccess.setAccess(['/a'])\n```\n由于权限列表中包含`/a`,则表示拥有此页面权限。\n\n#### 模糊匹配\n页面`@id.vue`会映射为动态路由`/:id`,想匹配此页面有两种办法:\n- **access.setAccess(['/:id'])**\n- **access.setAccess(['/*'])**\n\n第二种是模糊匹配,`*`表示任意路径。比如角色`admin`需要全部权限,则可以:\n```js\nexport default {\n access: {\n roles: {\n admin: [\"*\"]\n }\n }\n}\n```\n\n\n### 角色\n通常我们会用角色来控制权限,相应的Fes.js 用角色定义一组资源。当访问 Fes.js 应用时,使用插件提供的 API 设置用户的角色,角色对应的资源才可见,非角色对应的资源不可见。\n\n\n当然有时候业务比较复杂,角色对应的权限是动态的。不要怕!插件提供粒度更细的 API 来设置当前用户能访问的资源。\n\n\n## 启用方式\n在 `package.json` 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-access\": \"^2.0.0\"\n },\n}\n```\n\n## 配置\n\n### 编译时配置\n在执行 `fes dev` 或者 `fes build` 时,通过此配置生成运行时的代码,在配置文件`.fes.js` 中配置:\n```js\nexport default {\n access: {\n roles: {\n admin: [\"/\", \"/onepiece\", '/store']\n }\n }\n}\n```\n\n#### roles\n- **类型**:对象\n \n- **默认值**:`{}`\n\n- **详情**: \n \n 角色预定义列表。`key` 是角色 Id ,`value`是角色 Id 对应的资源列表。\n\n\n### 运行时配置\n在 `app.js` 中配置\n\n#### unAccessHandler\n- **类型**:`Function`\n \n- **默认值**:`null`\n\n- **详情**: \n \n 当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 `unAccessHandler` 函数。\n- **参数**\n - router:createRouter 创建的路由实例\n - to: 准备进入的路由\n - from:离开的路由\n - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)\n\n比如:\n```js\nexport const access = {\n unAccessHandler({ to, next }) {\n const accesssIds = accessApi.getAccess();\n if (to.path === '/404') {\n accessApi.setAccess(accesssIds.concat(['/404']));\n return next('/404');\n }\n if (!accesssIds.includes('/403')) {\n accessApi.setAccess(accesssIds.concat(['/403']));\n }\n next('/403');\n }\n};\n\n```\n\n#### noFoundHandler\n- **类型**:`Function`\n \n- **默认值**:`null`\n\n- **详情**: \n \n 当进入某个路由时,如果路由对应的页面不存在,则会调用 `noFoundHandler` 函数。\n- **参数**\n - router:createRouter 创建的路由实例\n - to: 准备进入的路由\n - from:离开的路由\n - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)\n\n比如:\n```js\nexport const access = {\n noFoundHandler({ next }) {\n const accesssIds = accessApi.getAccess();\n if (!accesssIds.includes('/404')) {\n accessApi.setAccess(accesssIds.concat(['/404']));\n }\n next('/404');\n }\n};\n\n```\n\n## API\n\n### access\n插件 API 通过 `@fesjs/fes` 导出:\n```js\nimport { access } from '@fesjs/fes'\n```\n\n#### access.hasAccess\n- **类型**:函数\n \n- **详情**: 判断某个资源是否可见。\n- **参数**:\n - accessId,资源Id\n- **返回值**:Boolean\n\n#### access.isDataReady\n- **类型**:函数\n \n- **详情**:可以用异步数据来设置权限,`isDataReady` 用来判断异步数据是否已经加载完毕。\n- **参数**:null\n- **返回值**:Boolean\n```js\nimport { access } from '@fesjs/fes';\nconsole.log(access.isDataReady())\n```\n\n#### access.setRole\n- **类型**:函数\n \n- **详情**:设置当前的角色。\n- **参数**:\n - roleId,角色Id,有两种类型:\n - String,对应着 `roles` 配置对象中的 `key`。\n - Promise,Promise resolve 的结果应对应着 `roles` 配置对象中的 `key`。\n```js\nimport { access } from '@fesjs/fes';\naccess.setRole(['admin'])\n```\n\n#### access.setAccess\n- **类型**:函数\n \n- **详情**:设置当前的角色。\n- **参数**:\n - accessIds,资源Id数组,有两种类型:\n - Array,数组项对应着 `roles` 配置对象中的 `key`。\n - Promise,Promise resolve 的结果应该是`Array`。\n```js\nimport { access } from '@fesjs/fes';\naccess.setAccess(['/a', '/b', '/c'])\n```\n\n#### access.getAccess\n- **类型**:函数\n \n- **详情**:返回当前可见的资源列表。\n- **参数**:null\n\n```js\nimport { access } from '@fesjs/fes';\naccess.getAccess();\n```\n\n### useAccess\n- **类型**:[composition]((https://v3.cn.vuejs.org/guide/composition-api-introduction.html)) 函数\n \n- **详情**:判断某个资源是否可见。\n- **参数**:\n - accessId,资源Id\n- **返回值**:`ref`\n \n```vue\n\n accessOnepicess\n\n\n```\n### v-access\n在指令 `v-access` 中传入 `accessId`,则当 `accessId` 拥有权限时显示DOM,当没有权限时隐藏此DOM。\n```vue\n\n accessOnepicess \n\n\n```\n\n\n### 匹配规则\n\n#### 全等匹配\n资源的匹配规则默认是使用全等匹配,比如页面 `pages/a.vue` 对应路由 `path` 是 `/a`,则 `/a` 就是页面的资源ID。如果我们设置:\n```js\naccess.setAccess(['/a'])\n```\n由于权限列表中包含`/a`,则表示拥有此页面权限。\n\n#### 模糊匹配\n页面`@id.vue`会映射为动态路由`/:id`,想匹配此页面有两种办法:\n- **access.setAccess(['/:id'])**\n- **access.setAccess(['/*'])**\n\n第二种是模糊匹配,`*`表示任意路径。比如角色`admin`需要全部权限,则可以:\n```js\nexport default {\n access: {\n roles: {\n admin: [\"*\"]\n }\n }\n}\n```\n\n\n### 角色\n通常我们会用角色来控制权限,相应的Fes.js 用角色定义一组资源。当访问 Fes.js 应用时,使用插件提供的 API 设置用户的角色,角色对应的资源才可见,非角色对应的资源不可见。\n\n\n当然有时候业务比较复杂,角色对应的权限是动态的。不要怕!插件提供粒度更细的 API 来设置当前用户能访问的资源。\n\n\n## 启用方式\n在 `package.json` 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-access\": \"^2.0.0\"\n },\n}\n```\n\n## 配置\n\n### 编译时配置\n在执行 `fes dev` 或者 `fes build` 时,通过此配置生成运行时的代码,在配置文件`.fes.js` 中配置:\n```js\nexport default {\n access: {\n roles: {\n admin: [\"/\", \"/onepiece\", '/store']\n }\n }\n}\n```\n\n#### roles\n- **类型**:对象\n \n- **默认值**:`{}`\n\n- **详情**: \n \n 角色预定义列表。`key` 是角色 Id ,`value`是角色 Id 对应的资源列表。\n\n\n### 运行时配置\n在 `app.js` 中配置\n\n#### unAccessHandler\n- **类型**:`Function`\n \n- **默认值**:`null`\n\n- **详情**: \n \n 当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 `unAccessHandler` 函数。\n- **参数**\n - router:createRouter 创建的路由实例\n - to: 准备进入的路由\n - from:离开的路由\n - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)\n\n比如:\n```js\nexport const access = {\n unAccessHandler({ to, next }) {\n const accesssIds = accessApi.getAccess();\n if (to.path === '/404') {\n accessApi.setAccess(accesssIds.concat(['/404']));\n return next('/404');\n }\n if (!accesssIds.includes('/403')) {\n accessApi.setAccess(accesssIds.concat(['/403']));\n }\n next('/403');\n }\n};\n\n```\n\n#### noFoundHandler\n- **类型**:`Function`\n \n- **默认值**:`null`\n\n- **详情**: \n \n 当进入某个路由时,如果路由对应的页面不存在,则会调用 `noFoundHandler` 函数。\n- **参数**\n - router:createRouter 创建的路由实例\n - to: 准备进入的路由\n - from:离开的路由\n - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)\n\n比如:\n```js\nexport const access = {\n noFoundHandler({ next }) {\n const accesssIds = accessApi.getAccess();\n if (!accesssIds.includes('/404')) {\n accessApi.setAccess(accesssIds.concat(['/404']));\n }\n next('/404');\n }\n};\n\n```\n\n## API\n\n### access\n插件 API 通过 `@fesjs/fes` 导出:\n```js\nimport { access } from '@fesjs/fes'\n```\n\n#### access.hasAccess\n- **类型**:函数\n \n- **详情**: 判断某个资源是否可见。\n- **参数**:\n - accessId,资源Id\n- **返回值**:Boolean\n\n#### access.isDataReady\n- **类型**:函数\n \n- **详情**:可以用异步数据来设置权限,`isDataReady` 用来判断异步数据是否已经加载完毕。\n- **参数**:null\n- **返回值**:Boolean\n```js\nimport { access } from '@fesjs/fes';\nconsole.log(access.isDataReady())\n```\n\n#### access.setRole\n- **类型**:函数\n \n- **详情**:设置当前的角色。\n- **参数**:\n - roleId,角色Id,有两种类型:\n - String,对应着 `roles` 配置对象中的 `key`。\n - Promise,Promise resolve 的结果应对应着 `roles` 配置对象中的 `key`。\n```js\nimport { access } from '@fesjs/fes';\naccess.setRole(['admin'])\n```\n\n#### access.setAccess\n- **类型**:函数\n \n- **详情**:设置当前的角色。\n- **参数**:\n - accessIds,资源Id数组,有两种类型:\n - Array,数组项对应着 `roles` 配置对象中的 `key`。\n - Promise,Promise resolve 的结果应该是`Array`。\n```js\nimport { access } from '@fesjs/fes';\naccess.setAccess(['/a', '/b', '/c'])\n```\n\n#### access.getAccess\n- **类型**:函数\n \n- **详情**:返回当前可见的资源列表。\n- **参数**:null\n\n```js\nimport { access } from '@fesjs/fes';\naccess.getAccess();\n```\n\n### useAccess\n- **类型**:[composition]((https://v3.cn.vuejs.org/guide/composition-api-introduction.html)) 函数\n \n- **详情**:判断某个资源是否可见。\n- **参数**:\n - accessId,资源Id\n- **返回值**:`ref`\n \n```vue\n\n accessOnepicess\n\n\n```\n### v-access\n在指令 `v-access` 中传入 `accessId`,则当 `accessId` 拥有权限时显示DOM,当没有权限时隐藏此DOM。\n```vue\n\n accessOnepicess \n\n\n```\n\n\n### 匹配规则\n\n#### 全等匹配\n资源的匹配规则默认是使用全等匹配,比如页面 `pages/a.vue` 对应路由 `path` 是 `/a`,则 `/a` 就是页面的资源ID。如果我们设置:\n```js\naccess.setAccess(['/a'])\n```\n由于权限列表中包含`/a`,则表示拥有此页面权限。\n\n#### 模糊匹配\n页面`@id.vue`会映射为动态路由`/:id`,想匹配此页面有两种办法:\n- **access.setAccess(['/:id'])**\n- **access.setAccess(['/*'])**\n\n第二种是模糊匹配,`*`表示任意路径。比如角色`admin`需要全部权限,则可以:\n```js\nexport default {\n access: {\n roles: {\n admin: [\"*\"]\n }\n }\n}\n```\n\n\n### 角色\n通常我们会用角色来控制权限,相应的Fes.js 用角色定义一组资源。当访问 Fes.js 应用时,使用插件提供的 API 设置用户的角色,角色对应的资源才可见,非角色对应的资源不可见。\n\n\n当然有时候业务比较复杂,角色对应的权限是动态的。不要怕!插件提供粒度更细的 API 来设置当前用户能访问的资源。\n\n\n## 启用方式\n在 `package.json` 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-access\": \"^2.0.0\"\n },\n}\n```\n\n## 配置\n\n### 编译时配置\n在执行 `fes dev` 或者 `fes build` 时,通过此配置生成运行时的代码,在配置文件`.fes.js` 中配置:\n```js\nexport default {\n access: {\n roles: {\n admin: [\"/\", \"/onepiece\", '/store']\n }\n }\n}\n```\n\n#### roles\n- **类型**:对象\n \n- **默认值**:`{}`\n\n- **详情**: \n \n 角色预定义列表。`key` 是角色 Id ,`value`是角色 Id 对应的资源列表。\n\n\n### 运行时配置\n在 `app.js` 中配置\n\n#### unAccessHandler\n- **类型**:`Function`\n \n- **默认值**:`null`\n\n- **详情**: \n \n 当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 `unAccessHandler` 函数。\n- **参数**\n - router:createRouter 创建的路由实例\n - to: 准备进入的路由\n - from:离开的路由\n - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)\n\n比如:\n```js\nexport const access = {\n unAccessHandler({ to, next }) {\n const accesssIds = accessApi.getAccess();\n if (to.path === '/404') {\n accessApi.setAccess(accesssIds.concat(['/404']));\n return next('/404');\n }\n if (!accesssIds.includes('/403')) {\n accessApi.setAccess(accesssIds.concat(['/403']));\n }\n next('/403');\n }\n};\n\n```\n\n#### noFoundHandler\n- **类型**:`Function`\n \n- **默认值**:`null`\n\n- **详情**: \n \n 当进入某个路由时,如果路由对应的页面不存在,则会调用 `noFoundHandler` 函数。\n- **参数**\n - router:createRouter 创建的路由实例\n - to: 准备进入的路由\n - from:离开的路由\n - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)\n\n比如:\n```js\nexport const access = {\n noFoundHandler({ next }) {\n const accesssIds = accessApi.getAccess();\n if (!accesssIds.includes('/404')) {\n accessApi.setAccess(accesssIds.concat(['/404']));\n }\n next('/404');\n }\n};\n\n```\n\n## API\n\n### access\n插件 API 通过 `@fesjs/fes` 导出:\n```js\nimport { access } from '@fesjs/fes'\n```\n\n#### access.hasAccess\n- **类型**:函数\n \n- **详情**: 判断某个资源是否可见。\n- **参数**:\n - accessId,资源Id\n- **返回值**:Boolean\n\n#### access.isDataReady\n- **类型**:函数\n \n- **详情**:可以用异步数据来设置权限,`isDataReady` 用来判断异步数据是否已经加载完毕。\n- **参数**:null\n- **返回值**:Boolean\n```js\nimport { access } from '@fesjs/fes';\nconsole.log(access.isDataReady())\n```\n\n#### access.setRole\n- **类型**:函数\n \n- **详情**:设置当前的角色。\n- **参数**:\n - roleId,角色Id,有两种类型:\n - String,对应着 `roles` 配置对象中的 `key`。\n - Promise,Promise resolve 的结果应对应着 `roles` 配置对象中的 `key`。\n```js\nimport { access } from '@fesjs/fes';\naccess.setRole(['admin'])\n```\n\n#### access.setAccess\n- **类型**:函数\n \n- **详情**:设置当前的角色。\n- **参数**:\n - accessIds,资源Id数组,有两种类型:\n - Array,数组项对应着 `roles` 配置对象中的 `key`。\n - Promise,Promise resolve 的结果应该是`Array`。\n```js\nimport { access } from '@fesjs/fes';\naccess.setAccess(['/a', '/b', '/c'])\n```\n\n#### access.getAccess\n- **类型**:函数\n \n- **详情**:返回当前可见的资源列表。\n- **参数**:null\n\n```js\nimport { access } from '@fesjs/fes';\naccess.getAccess();\n```\n\n### useAccess\n- **类型**:[composition]((https://v3.cn.vuejs.org/guide/composition-api-introduction.html)) 函数\n \n- **详情**:判断某个资源是否可见。\n- **参数**:\n - accessId,资源Id\n- **返回值**:`ref`\n \n```vue\n\n accessOnepicess\n\n\n```\n### v-access\n在指令 `v-access` 中传入 `accessId`,则当 `accessId` 拥有权限时显示DOM,当没有权限时隐藏此DOM。\n```vue\n\n accessOnepicess \n\n\n```\n\n\n### 匹配规则\n\n#### 全等匹配\n资源的匹配规则默认是使用全等匹配,比如页面 `pages/a.vue` 对应路由 `path` 是 `/a`,则 `/a` 就是页面的资源ID。如果我们设置:\n```js\naccess.setAccess(['/a'])\n```\n由于权限列表中包含`/a`,则表示拥有此页面权限。\n\n#### 模糊匹配\n页面`@id.vue`会映射为动态路由`/:id`,想匹配此页面有两种办法:\n- **access.setAccess(['/:id'])**\n- **access.setAccess(['/*'])**\n\n第二种是模糊匹配,`*`表示任意路径。比如角色`admin`需要全部权限,则可以:\n```js\nexport default {\n access: {\n roles: {\n admin: [\"*\"]\n }\n }\n}\n```\n\n\n### 角色\n通常我们会用角色来控制权限,相应的Fes.js 用角色定义一组资源。当访问 Fes.js 应用时,使用插件提供的 API 设置用户的角色,角色对应的资源才可见,非角色对应的资源不可见。\n\n\n当然有时候业务比较复杂,角色对应的权限是动态的。不要怕!插件提供粒度更细的 API 来设置当前用户能访问的资源。\n\n\n## 启用方式\n在 `package.json` 中引入依赖:\n```json\n{\n \"dependencies\": {\n \"@fesjs/fes\": \"^2.0.0\",\n \"@fesjs/plugin-access\": \"^2.0.0\"\n },\n}\n```\n\n## 配置\n\n### 编译时配置\n在执行 `fes dev` 或者 `fes build` 时,通过此配置生成运行时的代码,在配置文件`.fes.js` 中配置:\n```js\nexport default {\n access: {\n roles: {\n admin: [\"/\", \"/onepiece\", '/store']\n }\n }\n}\n```\n\n#### roles\n- **类型**:对象\n \n- **默认值**:`{}`\n\n- **详情**: \n \n 角色预定义列表。`key` 是角色 Id ,`value`是角色 Id 对应的资源列表。\n\n\n### 运行时配置\n在 `app.js` 中配置\n\n#### unAccessHandler\n- **类型**:`Function`\n \n- **默认值**:`null`\n\n- **详情**: \n \n 当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 `unAccessHandler` 函数。\n- **参数**\n - router:createRouter 创建的路由实例\n - to: 准备进入的路由\n - from:离开的路由\n - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)\n\n比如:\n```js\nexport const access = {\n unAccessHandler({ to, next }) {\n const accesssIds = accessApi.getAccess();\n if (to.path === '/404') {\n accessApi.setAccess(accesssIds.concat(['/404']));\n return next('/404');\n }\n if (!accesssIds.includes('/403')) {\n accessApi.setAccess(accesssIds.concat(['/403']));\n }\n next('/403');\n }\n};\n\n```\n\n#### noFoundHandler\n- **类型**:`Function`\n \n- **默认值**:`null`\n\n- **详情**: \n \n 当进入某个路由时,如果路由对应的页面不存在,则会调用 `noFoundHandler` 函数。\n- **参数**\n - router:createRouter 创建的路由实例\n - to: 准备进入的路由\n - from:离开的路由\n - next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)\n\n比如:\n```js\nexport const access = {\n noFoundHandler({ next }) {\n const accesssIds = accessApi.getAccess();\n if (!accesssIds.includes('/404')) {\n accessApi.setAccess(accesssIds.concat(['/404']));\n }\n next('/404');\n }\n};\n\n```\n\n## API\n\n### access\n插件 API 通过 `@fesjs/fes` 导出:\n```js\nimport { access } from '@fesjs/fes'\n```\n\n#### access.hasAccess\n- **类型**:函数\n \n- **详情**: 判断某个资源是否可见。\n- **参数**:\n - accessId,资源Id\n- **返回值**:Boolean\n\n#### access.isDataReady\n- **类型**:函数\n \n- **详情**:可以用异步数据来设置权限,`isDataReady` 用来判断异步数据是否已经加载完毕。\n- **参数**:null\n- **返回值**:Boolean\n```js\nimport { access } from '@fesjs/fes';\nconsole.log(access.isDataReady())\n```\n\n#### access.setRole\n- **类型**:函数\n \n- **详情**:设置当前的角色。\n- **参数**:\n - roleId,角色Id,有两种类型:\n - String,对应着 `roles` 配置对象中的 `key`。\n - Promise,Promise resolve 的结果应对应着 `roles` 配置对象中的 `key`。\n```js\nimport { access } from '@fesjs/fes';\naccess.setRole(['admin'])\n```\n\n#### access.setAccess\n- **类型**:函数\n \n- **详情**:设置当前的角色。\n- **参数**:\n - accessIds,资源Id数组,有两种类型:\n - Array,数组项对应着 `roles` 配置对象中的 `key`。\n - Promise,Promise resolve 的结果应该是`Array`。\n```js\nimport { access } from '@fesjs/fes';\naccess.setAccess(['/a', '/b', '/c'])\n```\n\n#### access.getAccess\n- **类型**:函数\n \n- **详情**:返回当前可见的资源列表。\n- **参数**:null\n\n```js\nimport { access } from '@fesjs/fes';\naccess.getAccess();\n```\n\n### useAccess\n- **类型**:[composition]((https://v3.cn.vuejs.org/guide/composition-api-introduction.html)) 函数\n \n- **详情**:判断某个资源是否可见。\n- **参数**:\n - accessId,资源Id\n- **返回值**:`ref`\n \n```vue\n\n accessOnepicess\n\n\n```\n### v-access\n在指令 `v-access` 中传入 `accessId`,则当 `accessId` 拥有权限时显示DOM,当没有权限时隐藏此DOM。\n```vue\n\n accessOnepicess \n\n
message: {{ t('hello') }}