Commit 0e63f39f authored by rockyl's avatar rockyl

更新mock数据

更改api接口
增加素材列表功能
增加视图编辑功能
增加无效页面自动跳转功能
增加本地版本记录
增加本地版本和服务器版本的选择
删除素材地址前缀改用全路径
parent d64a4c0f
...@@ -58,12 +58,12 @@ const data = { ...@@ -58,12 +58,12 @@ const data = {
{ {
"name": "bg.jpg", "name": "bg.jpg",
uuid:"a1", uuid:"a1",
"url": "/bg.jpg" "url": "http://0.0.0.0:4002/assets/bg.jpg"
}, },
{ {
"name": "btn-join.png", "name": "btn-join.png",
uuid:"a2", uuid:"a2",
"url": "/btn-join.png" "url": "http://0.0.0.0:4002/assets/btn-join.png"
} }
] ]
}; };
...@@ -71,7 +71,7 @@ const data = { ...@@ -71,7 +71,7 @@ const data = {
const resp = { const resp = {
"success": true, "success": true,
"data": { "data": {
"id": "6e1c9eadf8e28", "id": "6566c4a3f237",
"name": "测试", "name": "测试",
"creator": "卞龙亭", "creator": "卞龙亭",
"operator": "卞龙亭", "operator": "卞龙亭",
......
{
"success": true
}
\ No newline at end of file
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
"path": "^0.12.7", "path": "^0.12.7",
"querystringify": "^2.1.1", "querystringify": "^2.1.1",
"splitpanes": "^1.14.5", "splitpanes": "^1.14.5",
"uuid": "^3.3.3",
"vue": "^2.6.10", "vue": "^2.6.10",
"vue-i18n": "^8.0.0", "vue-i18n": "^8.0.0",
"vue-router": "^3.0.3", "vue-router": "^3.0.3",
......
<template> <template>
<div id="app" class="theme-light"> <div id="app" class="theme-light">
<router-view/> <router-view/>
<div class="invalid-route" v-if="invalidRoute"> <div class="invalid-route" v-if="ready && invalidRoute">
<p>无效的页面 {{cd}}秒后跳转</p> <p>无效的页面 {{cd}}秒后跳转</p>
</div> </div>
</div> </div>
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
data() { data() {
return { return {
cd: 5, cd: 5,
ready: false,
} }
}, },
computed: { computed: {
...@@ -25,13 +26,11 @@ ...@@ -25,13 +26,11 @@
$route: { $route: {
handler: function(val, oldVal){ handler: function(val, oldVal){
this.dealInvalidRoute(); this.dealInvalidRoute();
this.ready = true;
}, },
deep: true deep: true
} }
}, },
mounted() {
this.dealInvalidRoute();
},
methods: { methods: {
dealInvalidRoute(){ dealInvalidRoute(){
if (this.invalidRoute) { if (this.invalidRoute) {
......
...@@ -36,15 +36,15 @@ export async function deleteOne(id) { ...@@ -36,15 +36,15 @@ export async function deleteOne(id) {
}) })
} }
export async function getData(id) { export async function fetchOne(id) {
return await fetchApi('/api/project/query/data', { return await fetchApi('/api/project/query/data', {
params: {id}, params: {id},
method: 'get', method: 'get',
errMessage: 'Failed to get project', errMessage: 'Failed to fetch project',
}) })
} }
export async function updateOne(project) { export async function saveOne(project) {
return await fetchApi('/api/project/update', { return await fetchApi('/api/project/update', {
params: project, params: project,
method: 'post', method: 'post',
......
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
export const API_HOST = 'http://10.10.94.31:7777'; export const API_HOST = 'http://10.10.94.31:7777';
//export const API_HOST = 'http://localhost:3002'; //export const API_HOST = 'http://localhost:3002';
export const ASSETS_BASE = 'http://0.0.0.0:4002/assets';
export const UPLOAD_FILE_URL = API_HOST + '/api/uploadFile';
//文件类型图标 t表示展示缩略图 //文件类型图标 t表示展示缩略图
export const fileTypeIcon = { export const fileTypeIcon = {
......
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
"Confirm": "Confirm", "Confirm": "Confirm",
"Cancel": "Cancel", "Cancel": "Cancel",
"Save": "Save", "Save": "Save",
"Add": "Add",
"Delete": "Delete",
"Import": "Import",
"Export": "Export",
"Upload": "Upload",
"Failed to fetch": "Network error!", "Failed to fetch": "Network error!",
"In processing": "In processing", "In processing": "In processing",
"Projects": "Projects", "Projects": "Projects",
...@@ -15,6 +20,7 @@ ...@@ -15,6 +20,7 @@
"Template": "Template", "Template": "Template",
"Preparing": "Preparing…", "Preparing": "Preparing…",
"Deleting": "Deleting…", "Deleting": "Deleting…",
"Saving": "Saving…",
"Create project": "Create project", "Create project": "Create project",
"Creating project": "Creating project…", "Creating project": "Creating project…",
"Create project success": "Create project success", "Create project success": "Create project success",
...@@ -30,6 +36,12 @@ ...@@ -30,6 +36,12 @@
"Failed to delete project": "Failed to delete project", "Failed to delete project": "Failed to delete project",
"Failed to get project": "Failed to get project", "Failed to get project": "Failed to get project",
"Failed to save project": "Failed to save project", "Failed to save project": "Failed to save project",
"Save project successfully": "Save project successfully",
"Input view name": "Input view name",
"Invalid view name": "Invalid view name",
"Unsaved version found locally": "Unsaved version found locally, please select version to open",
"Local Version": "Local Version",
"Remote Version": "Remote Version",
"menu": { "menu": {
"save": "Save", "save": "Save",
"details": "Details", "details": "Details",
...@@ -38,6 +50,12 @@ ...@@ -38,6 +50,12 @@
"data-mapping": "DataMapping", "data-mapping": "DataMapping",
"exit": "Exit" "exit": "Exit"
}, },
"view_node_menu": {
"node": "Node",
"image": "Image",
"label": "Label",
"rect": "Rect"
},
"panes": { "panes": {
"Assets": "Assets", "Assets": "Assets",
"Inspector": "Inspector", "Inspector": "Inspector",
......
...@@ -7,6 +7,7 @@ import Vuex from 'vuex' ...@@ -7,6 +7,7 @@ import Vuex from 'vuex'
import {envStore} from "./modules/env"; import {envStore} from "./modules/env";
import {projectsStore} from "./modules/projects"; import {projectsStore} from "./modules/projects";
import {projectStore} from "./modules/project"; import {projectStore} from "./modules/project";
import SaveToLocalPlugin from "./save-to-local-plugin";
Vue.use(Vuex); Vue.use(Vuex);
...@@ -19,5 +20,14 @@ export default new Vuex.Store({ ...@@ -19,5 +20,14 @@ export default new Vuex.Store({
projects: projectsStore, projects: projectsStore,
project: projectStore, project: projectStore,
}, },
plugins: [
SaveToLocalPlugin({
mutationTypes: [
'addNode',
'deleteNode',
'addAsset',
]
})
]
}) })
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
* Created by rockyl on 2019-09-19. * Created by rockyl on 2019-09-19.
*/ */
import Vue from "vue";
import {projectApi} from "../../api"; import {projectApi} from "../../api";
import generateUUID from "uuid/v4";
export const projectStore = { export const projectStore = {
state: { state: {
...@@ -16,32 +18,91 @@ export const projectStore = { ...@@ -16,32 +18,91 @@ export const projectStore = {
} }
}, },
mutations: { mutations: {
updateProject(state, project) { updateProject(state, project) {
const {id, name, creator, data} = project; const {id, name, creator, data} = project;
state.id = id; state.id = id;
state.name = name; state.name = name;
state.creator = creator; state.creator = creator;
if(data){ if (data) {
const localData = state.data; const localData = state.data;
const {views, assets, dataMapping} = JSON.parse(data); const {views, assets, dataMapping} = JSON.parse(data);
if(!localData.views || localData.views.length === 0){ if (!localData.views || localData.views.length === 0) {
localData.views = views || []; Vue.set(localData, 'views', views || []);
} }
if(!localData.assets || localData.assets.length === 0){ if (!localData.assets || localData.assets.length === 0) {
localData.assets = assets || []; Vue.set(localData, 'assets', assets || []);
}
if (!localData.dataMapping || localData.dataMapping.length === 0) {
Vue.set(localData, 'dataMapping', dataMapping || []);
} }
if(!localData.dataMapping || localData.dataMapping.length === 0){
localData.dataMapping = dataMapping || [];
};
} }
}, },
addNode(state, {node, name, type}){
const child = {
name,
type,
};
if(node){
if(!node.children){
Vue.set(node, 'children', []);
}
node.children.push(child);
}else{
state.data.views.push(child);
}
},
deleteNode(state, {node, parentNode}){
const index = parentNode.children.indexOf(node);
parentNode.children.splice(index, 1);
},
addAsset(state, {url, file}){
state.data.assets.push({
name: file.name,
url,
uuid: generateUUID(),
})
},
},
getters: {
project(state){
const {id, name, creator, data} = state;
return {
id, name, creator,
data: JSON.stringify(data),
};
}
}, },
actions: { actions: {
async updateProject({commit}, projectID) { saveToLocal({getters}){
const project = await projectApi.getData(projectID); const {project} = getters;
localStorage.setItem('project-' + project.id, JSON.stringify(project));
},
localVersionExist({commit}, projectID){
let json = localStorage.getItem('project-' + projectID);
return !!json;
},
loadFromLocal({commit}, projectID){
let json = localStorage.getItem('project-' + projectID);
if(json){
const project = JSON.parse(json);
commit('updateProject', project);
}
},
deleteLocalVersion({state}, projectID){
localStorage.removeItem('project-' + projectID);
},
async loadFromRemote({commit, dispatch}, projectID) {
const project = await projectApi.fetchOne(projectID);
dispatch('deleteLocalVersion', projectID);
commit('updateProject', project); commit('updateProject', project);
}, },
async saveToRemote({state, dispatch, getters}) {
await projectApi.saveOne(getters.project);
dispatch('deleteLocalVersion', state.id);
},
}, },
}; };
/**
* Created by rockyl on 2019-09-25.
*/
export default function (options = {}) {
const {mutationTypes = []} = options;
return (store) => {
store.subscribe((mutation, state) => {
if (mutationTypes.includes(mutation.type)) {
store.dispatch('saveToLocal')
}
})
}
}
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
//background: gray;
.thumbnail { .thumbnail {
max-width: 60px; max-width: 60px;
...@@ -50,6 +49,29 @@ ...@@ -50,6 +49,29 @@
.name { .name {
text-align: center; text-align: center;
font-size: 12px; font-size: 12px;
width: 60px;
overflow: hidden;
}
}
.file-uploader {
width: 60px;
height: 60px;
margin: 5px 5px 5px;
border: 1px dashed $--color-text-secondary;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
.file-uploader-icon {
font-size: 28px;
color: $--color-text-regular;
width: 60px;
height: 60px;
line-height: 60px;
text-align: center;
} }
} }
} }
...@@ -80,13 +102,22 @@ ...@@ -80,13 +102,22 @@
.name-bar { .name-bar {
color: $--color-white; color: $--color-white;
align-self: flex-start; align-self: stretch;
.el-tag { .item{
width: 40px; display: flex;
text-align: right; .el-tag {
margin-right: 5px; width: 40px;
text-align: right;
margin-right: 5px;
}
.value{
width: 0;
flex: 1;
overflow-wrap: break-word;
}
} }
} }
.big-image { .big-image {
......
...@@ -22,5 +22,26 @@ ...@@ -22,5 +22,26 @@
width: 100%; width: 100%;
flex: 1; flex: 1;
} }
.el-tree-node__content:hover{
& > .tree-node > .more-button{
display: block;
}
}
.tree-node{
flex: 1;
flex-direction: row;
display: flex;
padding-right: 10px;
.node-name{
flex: 1;
}
.more-button{
display: none;
}
}
} }
} }
\ No newline at end of file
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
</template> </template>
<script> <script>
import {mapState, mapActions} from 'vuex' import {mapGetters, mapActions} from 'vuex'
import SplitPanes from 'splitpanes' import SplitPanes from 'splitpanes'
import ToolBar from "./Editor/ToolBar"; import ToolBar from "./Editor/ToolBar";
import Inspector from "./Editor/Inspector"; import Inspector from "./Editor/Inspector";
...@@ -46,15 +46,44 @@ ...@@ -46,15 +46,44 @@
panesConfig, panesConfig,
} }
}, },
mounted() { computed: {
...mapGetters([]),
},
async mounted() {
const {projectID} = this.$route.params; const {projectID} = this.$route.params;
if(projectID){ if (await this.localVersionExist(projectID)) {
playWaiting(this.updateProject(projectID), this.$t('Preparing')).catch(e => {}); this.$confirm(this.$t('Unsaved version found locally'), this.$t('Alert'), {
showClose: false,
closeOnClickModal: false,
closeOnPressEscape: false,
confirmButtonText: this.$t('Local Version'),
cancelButtonText: this.$t('Remote Version'),
type: 'warning'
}).then(() => {
this.loadLocalVersion(projectID);
}).catch((e) => {
if(e === 'cancel'){
this.loadRemoteVersion(projectID);
}else{
console.log(e);
}
});
}else{ }else{
this.$router.push({name: 'home'}) this.loadRemoteVersion(projectID);
} }
}, },
methods: { methods: {
loadLocalVersion(projectID) {
this.loadFromLocal(projectID)
},
loadRemoteVersion(projectID) {
if (projectID) {
playWaiting(this.loadFromRemote(projectID), this.$t('Preparing')).catch(e => {
});
} else {
this.$router.push({name: 'home'})
}
},
getSize(id, side) { getSize(id, side) {
let ratio = this.panesConfig[id]; let ratio = this.panesConfig[id];
return (side === 0 ? ratio : 1 - ratio) * 100 return (side === 0 ? ratio : 1 - ratio) * 100
...@@ -63,9 +92,17 @@ ...@@ -63,9 +92,17 @@
this.panesConfig[id] = configs[0].width / 100; this.panesConfig[id] = configs[0].width / 100;
localStorage.panesConfig = JSON.stringify(this.panesConfig); localStorage.panesConfig = JSON.stringify(this.panesConfig);
}, },
clickMenu(menuItem){ async clickMenu(menuItem) {
console.log(menuItem); switch (menuItem) {
switch(menuItem){ case 'save':
try {
await playWaiting(this.saveToRemote(), this.$t('Saving'));
this.$message({
message: this.$t('Save project successfully'),
type: 'success'
});
}catch (e) {}
break;
case 'details': case 'details':
this.$refs.projectDialogsDialog.show(); this.$refs.projectDialogsDialog.show();
break; break;
...@@ -75,7 +112,11 @@ ...@@ -75,7 +112,11 @@
} }
}, },
...mapActions([ ...mapActions([
'updateProject' 'localVersionExist',
'loadFromLocal',
'loadFromRemote',
"saveToLocal",
"saveToRemote",
]) ])
} }
} }
......
...@@ -2,10 +2,21 @@ ...@@ -2,10 +2,21 @@
<pane class="assets" icon="el-icon-s-shop" :title="$t('panes.Assets')"> <pane class="assets" icon="el-icon-s-shop" :title="$t('panes.Assets')">
<div class="container"> <div class="container">
<div class="header-bar"> <div class="header-bar">
<el-link>上传</el-link> <el-link>{{$t('Upload')}}</el-link>
</div> </div>
<el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden"> <el-scrollbar class="assets-scrollbar" wrap-class="wrap-x-hidden">
<div class="file-list"> <div class="file-list">
<el-upload
class="file-uploader"
:action="uploadFileUrl"
name="file"
multiple
:show-file-list="false"
:on-success="uploadFileSuccess"
:on-error="uploadFileError"
>
<i class="el-icon-plus file-uploader-icon"></i>
</el-upload>
<file-item v-for="file in assets" :data="file" :key="file.url" @show-file-details="showFileDetails"/> <file-item v-for="file in assets" :data="file" :key="file.url" @show-file-details="showFileDetails"/>
</div> </div>
</el-scrollbar> </el-scrollbar>
...@@ -15,10 +26,11 @@ ...@@ -15,10 +26,11 @@
</template> </template>
<script> <script>
import {mapState, mapActions} from 'vuex' import {mapState, mapMutations} from 'vuex'
import Pane from "../../components/Pane"; import Pane from "../../components/Pane";
import FileItem from "./Assets/FileItem"; import FileItem from "./Assets/FileItem";
import AssetsShow from "./Assets/AssetsShow"; import AssetsShow from "./Assets/AssetsShow";
import {UPLOAD_FILE_URL} from "../../config";
export default { export default {
name: "Assets", name: "Assets",
...@@ -27,6 +39,9 @@ ...@@ -27,6 +39,9 @@
return {} return {}
}, },
computed: { computed: {
uploadFileUrl(){
return UPLOAD_FILE_URL;
},
...mapState({ ...mapState({
assets: state => state.project.data.assets assets: state => state.project.data.assets
}), }),
...@@ -37,7 +52,24 @@ ...@@ -37,7 +52,24 @@
methods: { methods: {
showFileDetails(file){ showFileDetails(file){
this.$refs.assetsShow.show(file); this.$refs.assetsShow.show(file);
} },
uploadFileSuccess(response, file){
if(response.success){
console.log('upload success', response, file);
this.addAsset({
url: response.url,
file,
})
}else{
this.uploadFileError();
}
},
uploadFileError(){
console.log('upload error')
},
...mapMutations([
'addAsset'
]),
} }
} }
</script> </script>
......
...@@ -12,8 +12,14 @@ ...@@ -12,8 +12,14 @@
<div class="wrapper"> <div class="wrapper">
<el-button class="close-button" size="mini" circle icon="el-icon-close" @click="hide"></el-button> <el-button class="close-button" size="mini" circle icon="el-icon-close" @click="hide"></el-button>
<div class="name-bar"> <div class="name-bar">
<div><el-tag size="mini" type="success">name</el-tag>{{file.name}}</div> <div class="item">
<div><el-tag size="mini" type="success">url</el-tag>{{file.url}}</div> <el-tag size="mini" type="success">name</el-tag>
<span class="value">{{file.name}}</span>
</div>
<div class="item">
<el-tag size="mini" type="success">url</el-tag>
<span class="value">{{file.url}}</span>
</div>
</div> </div>
<el-image class="big-image" :src="imageUrl" fit="contain"/> <el-image class="big-image" :src="imageUrl" fit="contain"/>
<div class="operate-bar"> <div class="operate-bar">
...@@ -28,7 +34,6 @@ ...@@ -28,7 +34,6 @@
</template> </template>
<script> <script>
import {ASSETS_BASE} from "../../../config";
export default { export default {
name: "AssetsShow", name: "AssetsShow",
...@@ -40,7 +45,7 @@ ...@@ -40,7 +45,7 @@
}, },
computed: { computed: {
imageUrl(){ imageUrl(){
return ASSETS_BASE + this.file.url; return this.file.url;
}, },
}, },
methods: { methods: {
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
</template> </template>
<script> <script>
import {ASSETS_BASE, fileTypeIcon} from "../../../config"; import {fileTypeIcon} from "../../../config";
import path from "path"; import path from "path";
export default { export default {
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
return fileTypeIcon[this.ext] === 't'; return fileTypeIcon[this.ext] === 't';
}, },
thumbnailUrl(){ thumbnailUrl(){
return ASSETS_BASE + this.data.url; return this.data.url;
}, },
}, },
methods: { methods: {
......
...@@ -2,24 +2,47 @@ ...@@ -2,24 +2,47 @@
<pane class="views" icon="el-icon-s-grid" :title="$t('panes.Views')"> <pane class="views" icon="el-icon-s-grid" :title="$t('panes.Views')">
<div class="container"> <div class="container">
<div class="header-bar"> <div class="header-bar">
<el-link>导入</el-link> <el-link @click="toAddView">{{$t('Add')}}</el-link>
<el-link>导出</el-link> <el-link @click="toImportView">{{$t('Import')}}</el-link>
<el-link @click="toExportView">{{$t('Export')}}</el-link>
</div> </div>
<el-scrollbar class="tree-scrollbar" wrap-class="wrap-x-hidden"> <el-scrollbar class="tree-scrollbar" wrap-class="wrap-x-hidden">
<el-tree <el-tree
:data="views" :data="views"
:props="defaultProps" :props="defaultProps"
:expand-on-click-node="false" :expand-on-click-node="false"
draggable
highlight-current highlight-current
empty-text="" empty-text=""
/> @node-click="selectNode"
>
<div slot-scope="{ node, data }" class="tree-node">
<div class="node-name">
<span>{{data.name}}</span>
</div>
<el-dropdown class="more-button" size="mini" trigger="click"
@command="(command)=>{onMoreMenu(command, data, node)}">
<el-link icon="el-icon-more" :underline="false"/>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="delete">{{$t('Delete')}}</el-dropdown-item>
<el-dropdown-item v-for="(type, key, index) in $t('view_node_menu')"
:key="key"
:command="'add_node_' + key"
:divided="index===0">
{{$t('Add') + type}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-tree>
</el-scrollbar> </el-scrollbar>
</div> </div>
</pane> </pane>
</template> </template>
<script> <script>
import {mapState, mapActions} from 'vuex' import {mapState, mapMutations} from 'vuex'
import Pane from "../../components/Pane"; import Pane from "../../components/Pane";
export default { export default {
...@@ -37,6 +60,50 @@ ...@@ -37,6 +60,50 @@
...mapState({ ...mapState({
views: state => state.project.data.views views: state => state.project.data.views
}), }),
},
methods: {
toAddView() {
this.$prompt(this.$t('Input view name'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
inputPattern: /^.{1,64}$/,
inputErrorMessage: this.$t('Invalid view name'),
}).then(({value}) => {
this.addNode({
name: value,
type: 'node',
});
}).catch(() => {
});
},
toImportView() {
},
toExportView() {
},
selectNode(data, node, target) {
},
onMoreMenu(command, data, node) {
if (command === 'delete') {
this.deleteNode({
node: data,
parentNode: node.parent.data
});
} else if (command.startsWith('add_node_')) {
const type = command.substr('add_node_'.length);
this.addNode({
node: data,
type,
name: 'node',
})
}
},
...mapMutations([
"deleteNode",
"addNode",
])
} }
} }
</script> </script>
......
...@@ -7769,6 +7769,11 @@ uuid@^3.0.1, uuid@^3.3.2: ...@@ -7769,6 +7769,11 @@ uuid@^3.0.1, uuid@^3.3.2:
resolved "https://registry.npm.taobao.org/uuid/download/uuid-3.3.3.tgz?cache=0&sync_timestamp=1566221202613&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuuid%2Fdownload%2Fuuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" resolved "https://registry.npm.taobao.org/uuid/download/uuid-3.3.3.tgz?cache=0&sync_timestamp=1566221202613&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuuid%2Fdownload%2Fuuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
integrity sha1-RWjwIW54dg7h2/Ok0s9T4iQRKGY= integrity sha1-RWjwIW54dg7h2/Ok0s9T4iQRKGY=
uuid@^3.3.3:
version "3.3.3"
resolved "https://registry.npm.taobao.org/uuid/download/uuid-3.3.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuuid%2Fdownload%2Fuuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
integrity sha1-RWjwIW54dg7h2/Ok0s9T4iQRKGY=
validate-npm-package-license@^3.0.1: validate-npm-package-license@^3.0.1:
version "3.0.4" version "3.0.4"
resolved "https://registry.npm.taobao.org/validate-npm-package-license/download/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" resolved "https://registry.npm.taobao.org/validate-npm-package-license/download/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment