Commit 596fe898 authored by rockyl's avatar rockyl

菜单增加角标提示

素材项增加ext字段
素材展示层增加图片尺寸显示
素材项名字显示优化
暂时隐藏项目详情弹窗菜单
增加修改项目名功能
处理项目id不对的情况
所有路由返回home的地方由push改为replace
parent df3c7706
No preview for this file type
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
this.timer = setInterval(() => { this.timer = setInterval(() => {
if (this.cd <= 0) { if (this.cd <= 0) {
clearInterval(this.timer); clearInterval(this.timer);
this.$router.push({name: 'home'}); this.$router.replace({name: 'home'});
return; return;
} }
this.cd--; this.cd--;
......
<template>
<p>{{ $t('hello') }}</p>
</template>
<script>
export default {
name: 'HelloI18n'
}
</script>
<i18n>
{
"en": {
"hello": "Hello i18n in SFC!"
}
}
</i18n>
<template> <template>
<div class="menu"> <div class="menu">
<el-link @click="clickItem(key)" v-for="(item, key) of data" :key="key" :icon="item.icon"> <el-badge class="menu-item" v-for="(item, key) of data" :key="key" is-dot :hidden="!menuBadge(key)">
{{item.text}} <el-link @click="clickItem(key)" :icon="item.icon">
</el-link> {{item.text}}
</el-link>
</el-badge>
</div> </div>
</template> </template>
<script> <script>
import {mapGetters} from 'vuex'
export default { export default {
name: "SampleMenu", name: "SampleMenu",
props: ['data'], props: ['data'],
data() { data() {
return {} return {}
}, },
computed: {
...mapGetters([
'menuBadge',
]),
},
methods: { methods: {
clickItem(key) { clickItem(key) {
this.$emit('click-menu', key); this.$emit('click-menu', key);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
"Close": "Close", "Close": "Close",
"Save": "Save", "Save": "Save",
"Copy": "Copy", "Copy": "Copy",
"Exit": "Exit",
"Add": "Add", "Add": "Add",
"Delete": "Delete", "Delete": "Delete",
"Import": "Import", "Import": "Import",
...@@ -17,6 +18,7 @@ ...@@ -17,6 +18,7 @@
"No projects": "No projects", "No projects": "No projects",
"Create": "Create", "Create": "Create",
"Project details": "Project details", "Project details": "Project details",
"Project does not exist": "Project does not exist",
"Project name": "Project name", "Project name": "Project name",
"Data mapping": "Data mapping", "Data mapping": "Data mapping",
"Template": "Template", "Template": "Template",
...@@ -24,6 +26,9 @@ ...@@ -24,6 +26,9 @@
"Deleting": "Deleting…", "Deleting": "Deleting…",
"Saving": "Saving…", "Saving": "Saving…",
"Create project": "Create project", "Create project": "Create project",
"Rename project": "Rename project",
"Input project name": "Input project name",
"Invalid project name": "Invalid project name",
"Creating project": "Creating project…", "Creating project": "Creating project…",
"Create project success": "Create project success", "Create project success": "Create project success",
"Duplicate project": "Duplicate project", "Duplicate project": "Duplicate project",
...@@ -47,11 +52,12 @@ ...@@ -47,11 +52,12 @@
"Are you sure to delete this asset": "Are you sure to delete this asset", "Are you sure to delete this asset": "Are you sure to delete this asset",
"Are you sure to delete this link": "Are you sure to delete this link", "Are you sure to delete this link": "Are you sure to delete this link",
"Rename asset": "Rename asset", "Rename asset": "Rename asset",
"Input asset name": "Input asset name",
"Invalid asset name": "Invalid asset name", "Invalid asset name": "Invalid asset name",
"Copied field to clipboard": "Copied {field} to clipboard", "Copied field to clipboard": "Copied {field} to clipboard",
"Unsaved Alert": "You are leaving, but the project is not saved. Do you want to save it?",
"menu": { "menu": {
"save": "Save", "save": "Save",
"details": "Details",
"preview": "Preview", "preview": "Preview",
"publish": "Publish", "publish": "Publish",
"data-mapping": "DataMapping", "data-mapping": "DataMapping",
......
...@@ -23,6 +23,7 @@ export default new Vuex.Store({ ...@@ -23,6 +23,7 @@ export default new Vuex.Store({
plugins: [ plugins: [
SaveToLocalPlugin({ SaveToLocalPlugin({
mutationTypes: [ mutationTypes: [
'modifyProject',
'addNode', 'addNode',
'deleteNode', 'deleteNode',
'addAsset', 'addAsset',
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
*/ */
import Vue from "vue"; import Vue from "vue";
import path from "path";
import {projectApi} from "../../api"; import {projectApi} from "../../api";
import generateUUID from "uuid/v4"; import generateUUID from "uuid/v4";
...@@ -15,9 +16,13 @@ export const projectStore = { ...@@ -15,9 +16,13 @@ export const projectStore = {
views: [], views: [],
assets: [], assets: [],
dataMapping: [], dataMapping: [],
} },
dirty: false,
}, },
mutations: { mutations: {
setDirty(state, dirty = true) {
state.dirty = dirty;
},
updateProject(state, project) { updateProject(state, project) {
const {id, name, creator, data} = project; const {id, name, creator, data} = project;
state.id = id; state.id = id;
...@@ -37,6 +42,9 @@ export const projectStore = { ...@@ -37,6 +42,9 @@ export const projectStore = {
Vue.set(localData, 'dataMapping', dataMapping || []); Vue.set(localData, 'dataMapping', dataMapping || []);
} }
} }
},
modifyProject(state,) {
}, },
addNode(state, {node, name, type}) { addNode(state, {node, name, type}) {
const child = { const child = {
...@@ -58,8 +66,10 @@ export const projectStore = { ...@@ -58,8 +66,10 @@ export const projectStore = {
}, },
addAsset(state, {url, file}) { addAsset(state, {url, file}) {
const ext = path.extname(file.name);
state.data.assets.push({ state.data.assets.push({
name: file.name, name: path.basename(file.name, ext),
ext,
url, url,
uuid: generateUUID(), uuid: generateUUID(),
}) })
...@@ -79,9 +89,9 @@ export const projectStore = { ...@@ -79,9 +89,9 @@ export const projectStore = {
}, },
addDataMapping(state, link) { addDataMapping(state, link) {
if(link){ if (link) {
state.data.dataMapping.push(link); state.data.dataMapping.push(link);
}else{ } else {
state.data.dataMapping.push({ state.data.dataMapping.push({
name: '', name: '',
path: '', path: '',
...@@ -104,13 +114,23 @@ export const projectStore = { ...@@ -104,13 +114,23 @@ export const projectStore = {
id, name, creator, id, name, creator,
data: JSON.stringify(data), data: JSON.stringify(data),
}; };
},
menuBadge: (state) => (key) => {
let result = false;
switch (key) {
case 'save':
result = state.dirty;
break;
}
return result;
} }
}, },
actions: { actions: {
saveToLocal({getters}) { saveToLocal({getters, commit}) {
const {project} = getters; const {project} = getters;
localStorage.setItem('project-' + project.id, JSON.stringify(project)); localStorage.setItem('project-' + project.id, JSON.stringify(project));
commit('setDirty', true);
}, },
localVersionExist({commit}, projectID) { localVersionExist({commit}, projectID) {
let json = localStorage.getItem('project-' + projectID); let json = localStorage.getItem('project-' + projectID);
...@@ -121,15 +141,21 @@ export const projectStore = { ...@@ -121,15 +141,21 @@ export const projectStore = {
if (json) { if (json) {
const project = JSON.parse(json); const project = JSON.parse(json);
commit('updateProject', project); commit('updateProject', project);
commit('setDirty', true);
} }
}, },
deleteLocalVersion({state}, projectID) { deleteLocalVersion({state, commit}, projectID) {
localStorage.removeItem('project-' + projectID); localStorage.removeItem('project-' + projectID);
commit('setDirty', false);
}, },
async loadFromRemote({commit, dispatch}, projectID) { async loadFromRemote({commit, dispatch}, projectID) {
const project = await projectApi.fetchOne(projectID); const project = await projectApi.fetchOne(projectID);
dispatch('deleteLocalVersion', projectID); if(project){
commit('updateProject', project); dispatch('deleteLocalVersion', projectID);
commit('updateProject', project);
}else{
throw new Error('Project does not exist')
}
}, },
async saveToRemote({state, dispatch, getters}) { async saveToRemote({state, dispatch, getters}) {
await projectApi.saveOne(getters.project); await projectApi.saveOne(getters.project);
......
...@@ -27,13 +27,17 @@ ...@@ -27,13 +27,17 @@
.file-item { .file-item {
width: 60px; width: 60px;
padding: 5px 5px 0; padding: 5px;
color: $--color-text-regular; color: $--color-text-regular;
&:hover { &:hover {
& > .icon > .operate-bar { & > .icon > .operate-bar {
display: block; display: block;
} }
& > .name > .full-name {
display: block;
}
} }
.icon { .icon {
...@@ -72,14 +76,13 @@ ...@@ -72,14 +76,13 @@
position: relative; position: relative;
margin-top: 2px; margin-top: 2px;
&:hover {
& > .full-name {
display: block;
}
}
span { span {
word-break: break-all; word-break: break-all;
overflow:hidden;
text-overflow: ellipsis;
display:-webkit-box;
-webkit-line-clamp:2;
-webkit-box-orient:vertical;
} }
.full-name { .full-name {
...@@ -93,6 +96,7 @@ ...@@ -93,6 +96,7 @@
padding: 2px; padding: 2px;
z-index: 2; z-index: 2;
width: 95%; width: 95%;
line-height: 17px;
} }
} }
} }
...@@ -122,6 +126,10 @@ ...@@ -122,6 +126,10 @@
width: 100%; width: 100%;
flex: 1; flex: 1;
} }
.scrollbar-view{
padding-bottom: 5px;
}
} }
} }
...@@ -169,6 +177,11 @@ ...@@ -169,6 +177,11 @@
} }
} }
.image-info{
color: $__color-white;
font-size: 12px;
}
.operate-bar { .operate-bar {
} }
......
...@@ -19,10 +19,18 @@ ...@@ -19,10 +19,18 @@
.menu { .menu {
flex: 1; flex: 1;
a:not(:first-child) { .menu-item + .menu-item {
margin-left: 10px; margin-left: 10px;
} }
} }
.right-part{
color: $--color-text-regular;
.el-button{
padding: 2px;
}
}
} }
.pane-container { .pane-container {
...@@ -76,3 +84,7 @@ ...@@ -76,3 +84,7 @@
} }
} }
.project-details-dialog {
}
\ No newline at end of file
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
import ProjectDetailsDialog from "./Editor/dialogs/ProjectDetailsDialog"; import ProjectDetailsDialog from "./Editor/dialogs/ProjectDetailsDialog";
import DataMappingDialog from "./Editor/dialogs/DataMappingDialog"; import DataMappingDialog from "./Editor/dialogs/DataMappingDialog";
import {playWaiting} from "../utils"; import {playWaiting} from "../utils";
import i18n from "../i18n";
export default { export default {
name: 'Editor', name: 'Editor',
...@@ -62,13 +63,13 @@ ...@@ -62,13 +63,13 @@
}).then(() => { }).then(() => {
this.loadLocalVersion(projectID); this.loadLocalVersion(projectID);
}).catch((e) => { }).catch((e) => {
if(e === 'cancel'){ if (e === 'cancel') {
this.loadRemoteVersion(projectID); this.loadRemoteVersion(projectID);
}else{ } else {
console.log(e); console.log(e);
} }
}); });
}else{ } else {
this.loadRemoteVersion(projectID); this.loadRemoteVersion(projectID);
} }
}, },
...@@ -79,6 +80,12 @@ ...@@ -79,6 +80,12 @@
loadRemoteVersion(projectID) { loadRemoteVersion(projectID) {
if (projectID) { if (projectID) {
playWaiting(this.loadFromRemote(projectID), this.$t('Preparing')).catch(e => { playWaiting(this.loadFromRemote(projectID), this.$t('Preparing')).catch(e => {
this.$alert(this.$t('Project does not exist'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
callback: action=>{
this.$router.replace({name: 'home'});
}
});
}); });
} else { } else {
this.$router.push({name: 'home'}) this.$router.push({name: 'home'})
...@@ -92,16 +99,20 @@ ...@@ -92,16 +99,20 @@
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);
}, },
async saveProject() {
await playWaiting(this.saveToRemote(), this.$t('Saving'));
this.$message({
message: i18n.t('Save project successfully'), //因为message是异步出现,但是当路由回退的时候,this.i18n的实例已经置空,所以要用全局的i18n实例
type: 'success'
});
},
async clickMenu(menuItem) { async clickMenu(menuItem) {
switch (menuItem) { switch (menuItem) {
case 'save': case 'save':
try { try {
await playWaiting(this.saveToRemote(), this.$t('Saving')); this.saveProject();
this.$message({ } catch (e) {
message: this.$t('Save project successfully'), }
type: 'success'
});
}catch (e) {}
break; break;
case 'details': case 'details':
this.$refs.projectDialogsDialog.show(); this.$refs.projectDialogsDialog.show();
...@@ -109,8 +120,31 @@ ...@@ -109,8 +120,31 @@
case 'data-mapping': case 'data-mapping':
this.$refs.dataMappingDialog.show(); this.$refs.dataMappingDialog.show();
break; break;
case 'exit':
const {projectID} = this.$route.params;
if (await this.localVersionExist(projectID)) {
this.$confirm(this.$t('Unsaved Alert'), this.$t('Alert'), {
confirmButtonText: this.$t('Save'),
cancelButtonText: this.$t('Exit'),
type: 'warning'
}).then(() => {
try {
this.saveProject();
this.backToHome();
} catch (e) {
}
}).catch((e) => {
this.backToHome();
});
} else {
this.backToHome();
}
break;
} }
}, },
backToHome() {
this.$router.replace({name: 'home'});
},
...mapActions([ ...mapActions([
'localVersionExist', 'localVersionExist',
'loadFromLocal', 'loadFromLocal',
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<!--<div class="header-bar"> <!--<div class="header-bar">
<el-link>{{$t('Upload')}}</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" view-class="scrollbar-view">
<div class="file-list"> <div class="file-list">
<el-upload <el-upload
class="file-uploader" class="file-uploader"
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
> >
<i class="el-icon-plus file-uploader-icon"></i> <i class="el-icon-plus file-uploader-icon"></i>
</el-upload> </el-upload>
<file-item v-for="file in assets" :data="file" :key="file.url" @show-file-details="showFileDetails"/> <file-item v-for="(file, index) in assets" :data="file" :key="index + '_' + file.url" @show-file-details="showFileDetails"/>
</div> </div>
</el-scrollbar> </el-scrollbar>
</div> </div>
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
}, },
uploadFileSuccess(response, file){ uploadFileSuccess(response, file){
if(response.success){ if(response.success){
console.log('upload success', response, file); //console.log('upload success', response, file);
this.addAsset({ this.addAsset({
url: response.url, url: response.url,
file, file,
......
...@@ -21,7 +21,10 @@ ...@@ -21,7 +21,10 @@
<el-button slot="append" icon="el-icon-document-copy" @click="copyValue('url')"></el-button> <el-button slot="append" icon="el-icon-document-copy" @click="copyValue('url')"></el-button>
</el-input> </el-input>
</div> </div>
<el-image class="big-image" :src="imageUrl" fit="contain"/> <el-image class="big-image" :src="imageUrl" fit="contain" @load="onImageLoaded"/>
<div class="image-info">
<span class="size">{{size}}</span>
</div>
<div class="operate-bar"> <div class="operate-bar">
<el-button-group> <el-button-group>
</el-button-group> </el-button-group>
...@@ -41,10 +44,16 @@ ...@@ -41,10 +44,16 @@
return { return {
visible: false, visible: false,
file: null, file: null,
size: '',
}
},
watch: {
file(){
this.size = '';
} }
}, },
computed: { computed: {
imageUrl(){ imageUrl() {
return this.file.url; return this.file.url;
}, },
}, },
...@@ -66,10 +75,10 @@ ...@@ -66,10 +75,10 @@
afterLeave() { afterLeave() {
this.$emit('closed'); this.$emit('closed');
}, },
handleWrapperClick(){ handleWrapperClick() {
this.hide(); this.hide();
}, },
copyValue(field){ copyValue(field) {
copy(this.file[field]); copy(this.file[field]);
this.$message({ this.$message({
message: this.$t('Copied field to clipboard', {field}), message: this.$t('Copied field to clipboard', {field}),
...@@ -77,6 +86,10 @@ ...@@ -77,6 +86,10 @@
duration: 700, duration: 700,
}); });
}, },
onImageLoaded(e) {
const {width, height} = e.target;
this.size = width + ' x ' + height;
}
} }
} }
</script> </script>
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
</div> </div>
</div> </div>
<div class="name"> <div class="name">
<span>{{ellipsisName}}</span> <span>{{data.name}}</span>
<span class="full-name">{{data.name}}</span> <span class="full-name">{{data.name}}</span>
</div> </div>
</div> </div>
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
<script> <script>
import {mapMutations} from "vuex"; import {mapMutations} from "vuex";
import {fileTypeIcon} from "../../../config"; import {fileTypeIcon} from "../../../config";
import path from "path";
import {strEllipsis} from "../../../utils"; import {strEllipsis} from "../../../utils";
export default { export default {
...@@ -27,35 +26,27 @@ ...@@ -27,35 +26,27 @@
props: ['data'], props: ['data'],
data() { data() {
return { return {
ext: '',
} }
}, },
watch: { watch: {
data() {
this.updateExt();
}
},
mounted() {
this.updateExt();
}, },
computed: { computed: {
fileIcon() { fileIcon() {
return 'icon-' + fileTypeIcon[this.ext]; return 'icon-' + fileTypeIcon[this.data.ext];
}, },
showThumbnail() { showThumbnail() {
return fileTypeIcon[this.ext] === 't'; return fileTypeIcon[this.data.ext] === 't';
}, },
thumbnailUrl() { thumbnailUrl() {
return this.data.url; return this.data.url;
}, },
ellipsisName(){ ellipsisName(){
return strEllipsis(this.data.name, 14, 8); return strEllipsis(this.data.name, 14, 5);
}, },
}, },
methods: { methods: {
updateExt() {
this.ext = path.extname(this.data.name);
},
onDbclick() { onDbclick() {
this.$emit('show-file-details', this.data); this.$emit('show-file-details', this.data);
}, },
...@@ -73,7 +64,7 @@ ...@@ -73,7 +64,7 @@
}); });
}, },
onClickEdit() { onClickEdit() {
this.$prompt(this.$t('Input view name'), this.$t('Rename asset'), { this.$prompt(this.$t('Input asset name'), this.$t('Rename asset'), {
inputValue: this.data.name, inputValue: this.data.name,
confirmButtonText: this.$t('Confirm'), confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'), cancelButtonText: this.$t('Cancel'),
......
<template> <template>
<div class="tool-bar"> <div class="tool-bar">
<sample-menu :data="menu" @click-menu="clickMenu"/> <sample-menu :data="menu" @click-menu="clickMenu"/>
<div> <div class="right-part">
[{{project.name}}] <span>
[{{project.name}}]
<el-button size="mini" circle icon="el-icon-edit" @click="editProjectName"/>
</span>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import {mapState, mapActions} from 'vuex' import {mapState, mapActions, mapMutations} from 'vuex'
import SampleMenu from "../../components/SampleMenu"; import SampleMenu from "../../components/SampleMenu";
export default { export default {
...@@ -32,7 +35,24 @@ ...@@ -32,7 +35,24 @@
methods: { methods: {
clickMenu(menuItem) { clickMenu(menuItem) {
this.$emit('click-menu', menuItem); this.$emit('click-menu', menuItem);
} },
editProjectName(){
this.$prompt(this.$t('Input project name'), this.$t('Rename project'), {
inputValue: this.project.name,
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
closeOnClickModal: false,
inputPattern: /^.{1,64}$/,
inputErrorMessage: this.$t('Invalid project name'),
}).then(({value}) => {
this.$set(this.project, 'name', value);
this.modifyProject();
}).catch(() => {
});
},
...mapMutations([
'modifyProject'
]),
} }
} }
</script> </script>
......
<template> <template>
<el-dialog :title="$t('Project details')" width="70%" :visible.sync="visible" @open="onOpen" <el-dialog :title="$t('Project details')" width="70%" :visible.sync="visible" @open="onOpen"
:close-on-click-modal="false"
:append-to-body="true"> :append-to-body="true">
<div class="project-details-dialog">
<el-form v-model="project">
<el-form-item prop="">
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button size="mini" @click="visible=false">{{$t('Cancel')}}</el-button> <el-button size="mini" @click="visible=false">{{$t('Cancel')}}</el-button>
<el-button size="mini" type="primary" @click="onConfirm">{{$t('Save')}}</el-button> <el-button size="mini" type="primary" @click="onConfirm">{{$t('Save')}}</el-button>
...@@ -10,6 +17,8 @@ ...@@ -10,6 +17,8 @@
</template> </template>
<script> <script>
import {mapState} from 'vuex';
export default { export default {
name: "ProjectDetailsDialog", name: "ProjectDetailsDialog",
data() { data() {
...@@ -17,6 +26,11 @@ ...@@ -17,6 +26,11 @@
visible: false, visible: false,
} }
}, },
computed: {
...mapState([
'project',
]),
},
methods: { methods: {
show(){ show(){
this.visible = true; this.visible = true;
......
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