Commit 3c50201e authored by 任建锋's avatar 任建锋

合并publish

parents e7ec6f42 53b816a1
module.exports = {
region: 'oss-cn-hangzhou',
id: 'LTAIqO2wblIxQvwc',
secret: '4brsaSRbRpjxw3oDIxJi6bNMcndIR6',
id: 'LTAI4Fw25WcfcGv7FvcHoiHK',
secret: 'NZk1NtT9J5HFaAolNbtQdzTzLLvLYm',
bucket: 'duiba',
output: '/editor/zeroing/v1',
exclude: /.js.map$/,
internal: true,
};
......@@ -33,10 +33,10 @@ export async function fetchApi(uri, {params, method = 'get', auth = true, judgeS
const options = {
method,
headers: {},
//credentials: 'include',
credentials: 'include',
};
if (auth) {
options.headers.authorization = 'Bearer ' + window['zeroing_token'];
//options.headers.authorization = 'Bearer ' + window['zeroing_token'];
}
if (params) {
if (method.toLowerCase() === 'post') {
......@@ -69,12 +69,14 @@ export async function fetchApi(uri, {params, method = 'get', auth = true, judgeS
}
const response = await fetch(url, options);
if (response.status === 401) {
location.href = '/admin/permission';
const respText = await response.text();
if (response.status === 310) { //客户端重定向,用于跨域重定向
location.href = respText;
}
const jsonObj = await response.json();
//console.log(jsonObj);
try {
let jsonObj = JSON.parse(respText);
if (judgeSuccess) {
if (jsonObj.success) {
return jsonObj.data;
......@@ -83,5 +85,8 @@ export async function fetchApi(uri, {params, method = 'get', auth = true, judgeS
return jsonObj;
}
throw new ApiError('call api failed', jsonObj.code, errMessage, jsonObj.details);
return Promise.reject(new ApiError('call api failed', jsonObj.code, errMessage, jsonObj.details));
}catch (e) {
return Promise.reject(e);
}
}
......@@ -4,6 +4,15 @@
import {fetchApi} from "./common";
let devPersons;
export async function getDevPersons() {
if (!devPersons) {
devPersons = await fetchApi('/api/editor/devPersons');
}
return devPersons
}
export async function importView(file) {
const response = await fetchApi('/api/parsePSD', {
params: {
......@@ -12,8 +21,8 @@ export async function importView(file) {
method: 'post',
contentType: 'form-data',
errMessage: 'Failed to import view',
})
response.__originFile = file
});
response.__originFile = file;
return response
}
......@@ -25,13 +34,13 @@ export async function uploadView(file) {
method: 'post',
contentType: 'form-data',
errMessage: 'Failed to upload view',
})
});
response.__originFile = file;
return response
}
export async function uploadFile(file, compress = false, uuid) {
let params = { file }
let params = {file};
if (compress) {
params.compress = true
}
......@@ -43,7 +52,7 @@ export async function uploadFile(file, compress = false, uuid) {
method: 'post',
contentType: 'form-data',
errMessage: 'Failed to upload file',
})
response.__originFile = file
});
response.__originFile = file;
return response
}
......@@ -5,32 +5,15 @@
import {fetchApi} from "./common";
import {getCookie} from "./utils";
export async function saveSkins(params) {
return await fetchApi(`/polaris/autoSaveSkins`, {
export async function saveSkin(params) {
return await fetchApi(`/polaris/saveSkin`, {
params,
method: 'post'
})
}
export async function getProjectSkins(projectId, env) {
return await fetchApi(`/polaris/getProjectSkins?projectId=${projectId}&env=${env}`, {
method: 'post',
params: {
cookie: {
dev: getCookie('sso_ticket'),
test: localStorage.getItem('ticket'),
prod: localStorage.getItem('prod_ticket'),
}
}
})
}
export async function getTestEnvTicket() {
return await fetchApi(`/polaris/getTestEnvTicket`, { method: 'get' })
}
export async function getProdTicket() {
return await fetchApi(`/polaris/getProdTicket`, { method: 'get' })
export async function getSkins(projectId, env) {
return await fetchApi(`/polaris/getSkins?projectId=${projectId}&env=${env}`)
}
export async function sendDingTalk() {
......
......@@ -7,6 +7,14 @@
import { fetchApi } from "./common"
import { getCookie } from './utils'
/**
* 获取项目列表
* @param keyword
* @param currentPage
* @param pageSize
* @param onlyMine
* @return {Promise<*|any>}
*/
export async function fetchAll(keyword, currentPage, pageSize, onlyMine) {
return await fetchApi('/api/project/query', {
params: {name: keyword, currentPage, pageSize, isAll: onlyMine ? 0 : 1 },
......@@ -14,6 +22,11 @@ export async function fetchAll(keyword, currentPage, pageSize, onlyMine) {
})
}
/**
* 创建一个项目
* @param project
* @return {Promise<*|any>}
*/
export async function createOne(project) {
return await fetchApi('/api/project/create', {
params: project,
......@@ -22,6 +35,11 @@ export async function createOne(project) {
})
}
/**
* 复制一个项目
* @param project
* @return {Promise<*|any>}
*/
export async function duplicateOne(project) {
return await fetchApi('/api/project/copy', {
params: project,
......@@ -30,6 +48,11 @@ export async function duplicateOne(project) {
})
}
/**
* 删除一个项目
* @param id
* @return {Promise<*|any>}
*/
export async function deleteOne(id) {
return await fetchApi('/api/project/delete', {
params: { id },
......@@ -38,6 +61,11 @@ export async function deleteOne(id) {
})
}
/**
* 获取一个项目
* @param id
* @return {Promise<*|any>}
*/
export async function fetchOne(id) {
return await fetchApi('/api/project/query/data', {
params: { id },
......@@ -46,6 +74,11 @@ export async function fetchOne(id) {
})
}
/**
* 从历史版本打开
* @param dataUrl
* @return {Promise<*|any>}
*/
export async function fetchOneFromDataUrl(dataUrl) {
return await fetchApi(dataUrl, {
auth: false,
......@@ -54,6 +87,13 @@ export async function fetchOneFromDataUrl(dataUrl) {
})
}
/**
* 获取历史保存列表
* @param id
* @param currentPage
* @param pageSize
* @return {Promise<*|any>}
*/
export async function fetchHistory(id, currentPage, pageSize) {
return await fetchApi('/api/project/history', {
params: { id, currentPage, pageSize },
......@@ -62,8 +102,14 @@ export async function fetchHistory(id, currentPage, pageSize) {
})
}
/**
* 保存项目
* @param project
* @param remark
* @return {Promise<*|any>}
*/
export async function saveOne(project, remark) {
project.remark = remark
project.remark = remark;
return await fetchApi('/api/project/update', {
params: project,
method: 'post',
......@@ -71,6 +117,13 @@ export async function saveOne(project, remark) {
})
}
/**
* 打包项目
* @param id
* @param debug
* @param packedAssets
* @return {Promise<*|any>}
*/
export async function pack(id, debug, packedAssets) {
return await fetchApi('/api/project/pack', {
params: { id, debug, packedAssets },
......@@ -78,3 +131,17 @@ export async function pack(id, debug, packedAssets) {
errMessage: 'Failed to pack project',
})
}
/**
* 修改开发人员名单
* @param id
* @param operators
* @return {Promise<*|any|>}
*/
export async function updateOperators(id, operators) {
return await fetchApi('/api/project/updateOperators', {
params: { id, operators },
method: 'post',
errMessage: 'Failed to update operator',
})
}
<template>
<el-form class="props-editor" v-if="data&&meta" v-model="data.props" size="mini" :label-width="labelWidth"
<el-form class="props-editor" v-if="data&&meta" v-model="data" size="mini" :label-width="labelWidth"
label-position="right" @submit.native.prevent>
<component v-for="(property, key) in meta.props"
:key="key"
:is="mode + '-input-wrapper'"
:editable="editable"
:switchable="switchable"
:mode="mode"
:value="data[key]"
:container="data"
:property="property"
:propertyName="key"
@cmd-prop-change="onCmdPropChanged"
>
<component
:is="getInput(property)"
:container="data.props"
:value="data.props[key]"
:container="data"
:value="data[key]"
:propertyName="key"
:property="property"
:key="key"
:editable="editable"
:linkable="linkable"
@input="onInput"
/>
</component>
</el-form>
</template>
......@@ -19,6 +31,7 @@
import {
NumberInput,
StringInput,
TextInput,
EnumInput,
BooleanInput,
ColorInput,
......@@ -27,12 +40,17 @@
MapInput,
DynamicInput,
Vector2Input,
SourceInput,
} from "./inputs";
import {parseType} from 'props-compute'
import LinkableInputWrapper from "./inputs/LinkableInputWrapper";
import CmdInputWrapper from "./inputs/CmdInputWrapper";
import SampleInputWrapper from "./inputs/SampleInputWrapper";
const inputMapping = {
number: 'NumberInput',
string: 'StringInput',
text: 'TextInput',
enum: 'EnumInput',
boolean: 'BooleanInput',
color: 'ColorInput',
......@@ -42,11 +60,13 @@
map: 'MapInput',
vector2: 'Vector2Input',
array: 'StringInput',
source: 'SourceInput',
};
export default {
name: "PropsEditor",
components: {
SourceInput,
DynamicInput,
MapInput,
NodeSelectInput,
......@@ -56,10 +76,16 @@
EnumInput,
NumberInput,
StringInput,
Vector2Input
TextInput,
Vector2Input,
LinkableInputWrapper,
CmdInputWrapper,
SampleInputWrapper,
},
data() {
return {}
return {
}
},
props: {
labelWidth: {
......@@ -70,10 +96,14 @@
type: Boolean,
default: true
},
linkable: {
switchable: {
type: Boolean,
default: false
},
mode: {
type: String,
default: 'sample',
},
data: {
type: Object,
},
......@@ -81,6 +111,11 @@
type: Object,
},
},
watch: {
data(v){
}
},
methods: {
getInput(property) {
let {type} = parseType(property.type);
......@@ -92,6 +127,10 @@
} else {
this.$set(container, propName, value);
}
this.$emit('input', value, container, propName, oldValue);
},
onCmdPropChanged(value, container, propName, oldValue){
this.$emit('input', value, container, propName, oldValue);
},
}
}
......
<template>
<div :draggable="draggable" @dragstart="onDragstart" ref="playerContainer" :style="style">
</div>
</template>
<script>
import {mapActions} from 'vuex';
import {Player} from 'svgaplayerweb';
export default {
name: "SvgaPreviewView",
props: {
maxWidth: {
type: Number
},
maxHeight: {
type: Number
},
src: {
type: String
},
draggable: {
type: Boolean,
default: false,
},
},
data() {
return {
videoSize: {
width: 0,
height: 0,
}
}
},
computed: {
style() {
const {width, height} = this.videoSize;
return {
width: (this.maxWidth || width) + 'px',
height: (this.maxHeight || height) + 'px',
}
},
size() {
const {width, height} = this.videoSize;
return `${width}x${height}`;
},
},
watch: {
src(v, oldv) {
if(v !== oldv){
this.update();
}
}
},
mounted() {
let container = this.$refs.playerContainer;
this.player = new Player(container);
this.update();
},
methods: {
onDragstart(e){
this.$emit('dragstart', e);
},
async update() {
const player = this.player;
player.clear();
if(this.src){
let videoItem = await this.loadSvga(this.src);
this.videoSize.width = videoItem.videoSize.width;
this.videoSize.height = videoItem.videoSize.height;
this.$nextTick(() => {
player.setVideoItem(videoItem);
player.startAnimation();
});
}
},
...mapActions([
'loadSvga'
]),
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<el-select v-model="selected" :filterable="filterable" allow-create placeholder="请选择">
<el-option
v-for="item in props"
:style="`${optionStyle}${item.value}`"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
<script>
import optionMapper from './optionMapper';
export default {
props: {
/*
* fontFamily: 字体
* fontSize: 字体大小选择列表
* borderStyle: 边框类型
* animeCount: 动效次数
*/
optionType: {
type: String,
default: 'fontSize'
},
optionList: Array, // 若有 optionList 则groupType 不起作用
value: [String, Number, Boolean],
filterable: Boolean
},
data() {
return {
selected: this.value || '',
optionMapper
};
},
computed: {
props() {
return this.optionList || this.optionMapper[this.optionType];
},
optionStyle() {
return this.optionType === 'fontFamily' ? 'font-family: ' : '';
}
},
watch: {
selected(v) {
this.$emit('change', v);
}
}
};
</script>
import _ from 'lodash'
const rangeStep = (start, step, stop) =>
_.map(
_.range(0, (1 + (stop - start) / step) >>> 0),
(n) => (start + step * n).toFixed(0),
);
export default {
fontFamily: [
{
label: '默认字体',
value: ''
},
{ label: '微软雅黑', value: 'Microsoft Yahei' },
// { label: '方正黑体简体', value: 'FZHei-B01S' },
// { label: '方正楷体简体', value: 'FZKai-Z03S' },
// { label: '方正书宋简体', value: 'FZShuSong-Z01S' },
// { label: '方正仿宋简体', value: 'FZFangSong-Z02S' },
// { label: '思源极细体', value: 'NotoSansSC-Thin' },
// { label: '思源细体', value: 'NotoSansSC-Light' },
// { label: '思源正常', value: 'NotoSansSC-DemiLight' },
// { label: '思源常规', value: 'NotoSansSC-Regular' },
// { label: '思源中等粗体', value: 'NotoSansSC-Medium' },
// { label: '思源粗体', value: 'NotoSansSC-Bold' },
// { label: '思源特粗', value: 'NotoSansSC-Black' },
{ label: '宋体', value: 'SimSun' },
// { label: '黑体', value: 'SimHei' },
{ label: 'fantasy', value: 'fantasy' },
{ label: 'Cursive', value: 'Cursive' }
],
borderStyle: [
{ label: '实线', value: 'solid' },
{ label: '点线', value: 'dotted' },
{ label: '虚线', value: 'dashed' },
{ label: '双线', value: 'double' }
],
backgroundSize: [
{ label: '全覆盖', value: 'cover' },
{ label: '完全显示图片', value: 'contain' }
],
backgroundRepeat: [
{ label: '显示一次', value: 'no-repeat' },
{ label: '重复', value: 'repeat' },
{ label: '水平方向重复', value: 'repeat-x' },
{ label: '垂直方向重复', value: 'repeat-y' }
],
backgroundPosition: [
{ label: '居中', value: 'center' },
{ label: '左上', value: 'top left' },
{ label: '顶部居中', value: 'top center' },
{ label: '右上', value: 'top right' },
{ label: '靠左', value: 'center left' },
{ label: '靠右', value: 'center right' },
{ label: '左下', value: 'bottom left' },
{ label: '底部居中', value: 'bottom center' },
{ label: '右下', value: 'bottom right' }
],
weekIndex: [
{ label: '周日', value: 0 },
{ label: '周一', value: 1 },
{ label: '周二', value: 2 },
{ label: '周三', value: 3 },
{ label: '周四', value: 4 },
{ label: '周五', value: 5 },
{ label: '周六', value: 6 }
],
fontSize: rangeStep(12, 1, 120).map((v) => ({ value: v, label: v })),
animeCount: rangeStep(1, 1, 10)
.map((v) => ({ value: v, label: v }))
.concat({ label: '无限循环', value: true })
};
<template>
<el-slider class="zero-slider" v-model="swvalue" :min="min" :max="max" :step="step" show-input :show-input-controls="false" input-size="mini"></el-slider>
</template>
<style>
/* .zero-slider > .el-slider__input > span {
display: none;
}
.zero-slider > .el-slider__input input {
padding: 0 10px;
} */
/* .zero-slider {
position: relative;
}
.zero-slider > .el-slider__input {
position: absolute;
left: 110%;
top: -250%;
width: 60px;
}
.zero-slider > .el-slider__input input {
padding: 0 10px;
} */
</style>
<script>
export default {
props: {
min: Number,
max: Number,
value: [String, Number, Boolean],
step: Number
},
data() {
return {
swvalue: this.value || 0
};
},
watch: {
swvalue(v) {
this.$emit('change', v);
}
}
};
</script>
<template>
<el-popover
placement="top"
trigger="hover"
width="auto"
:disabled="!legalUrl"
:content="url"
>
<img style="max-width: 200px;" v-if="url" :src="url" alt=""/>
<el-input v-model="swvalue" slot="reference" @drop.native="drop" @dragover.native="dragOver">
<el-button slot="append" icon="el-icon-aim" @click="locateAsset"></el-button>
</el-input>
</el-popover>
</template>
<style>
</style>
<script>
import events from "@/global-events.js"
import {assetScheme} from "../../../utils";
export default {
props: {
value: [String, Number, Boolean]
},
data() {
return {
swvalue: this.value || ''
};
},
watch: {
swvalue(v) {
this.$emit('change', v);
}
},
methods: {
drop(e) {
if (this.$store.state.project.dragUUID) {
console.log('native drop', this.$store.state);
this.swvalue = assetScheme + this.$store.state.project.dragUUID
}
},
dragOver(e) {
e.preventDefault();
},
locateAsset() {
let uuid = this.swvalue ? this.swvalue.split('//')[1] : null;
if (uuid) {
let asset = this.$store.state.project.data.assets.find(a => a.uuid === uuid);
if (asset) {
events.$emit('select-asset-item', asset);
}
}
}
},
computed: {
url: function () {
if (this.swvalue) {
if (this.swvalue.indexOf(assetScheme) > -1) {
let uuid = this.swvalue.split('//')[1];
let _ass = this.$store.state.project.data.assets.find(a => a.uuid === uuid);
return _ass ? _ass.url : '';
} else {
return this.swvalue;
}
} else {
return '';
}
},
legalUrl: function () {
return (this.swvalue + '').indexOf('//') > -1;
}
}
};
</script>
<template>
<el-switch v-model="swvalue"></el-switch>
</template>
<style>
</style>
<script>
export default {
props: {
value: [String, Number, Boolean],
},
data() {
return {
swvalue: true
};
},
mounted() {
console.log('switch created', this.swvalue);
},
watch: {
swvalue(v) {
console.log('switch value change', this.swvalue);
this.$emit('change', v);
}
}
};
</script>
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property" :propertyName="propertyName">
<component :is="mode + '-input-wrapper'" :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property" :propertyName="propertyName">
<el-select :disabled="!editable" :value="editValue" filterable @input="onInput" :placeholder="property.default" class="el-select">
<el-option
v-for="(item, key) in assets"
......@@ -9,17 +9,18 @@
<span>{{item.name}}</span>
</el-option>
</el-select>
</input-wrapper>
</component>
</template>
<script>
import {mapMutations} from 'vuex'
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
export default {
name: "AssetInput",
components: {InputWrapper,},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
computed: {
editValue() {
return this.value === undefined ? this.property.default : this.value;
......
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property" :propertyName="propertyName">
<el-switch :disabled="!editable" :value="editValue" @input="onInput"
class="picker"></el-switch>
</input-wrapper>
</template>
<script>
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
export default {
name: "BooleanInput",
components: {InputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
data() {
return {}
},
......@@ -32,6 +31,7 @@
<style scoped>
.picker {
flex: 1;
float: right;
margin-top: 4px;
}
......
<template>
<el-form-item class="input-wrapper" :label="propertyName" content-float="right"
:content-width="contentWidth" :labelOffsetTop="labelOffsetTop">
<el-tooltip placement="left" class="label" slot="label" trigger="hover" :open-delay="500" :enterable="false">
<div slot="content" class="property-name-popover">
<p>{{$t('Alias')}}{{property.alias}}</p>
<p>{{$t('Name')}}{{propertyName}}</p>
</div>
<div>
<p class="alias">{{property.alias||propertyName}}</p>
<p class="property-name">{{propertyName}}</p>
</div>
</el-tooltip>
<template v-if="cmdMode">
<el-input class="cmd-input" placeholder="exp" :value="cmdValue" @input="onInputCmdValue"
@change="saveCmdValue"></el-input>
</template>
<template v-else>
<slot></slot>
</template>
<el-link style="padding: 3px;" icon="el-icon-link" :underline="false"
v-if="switchable"
:type="cmdMode ? 'success' : 'default'" :disabled="!editable" @click="onClickLink"/>
</el-form-item>
</template>
<script>
import ElFormItem from "./form-item";
import {cmdOldPrefix, cmdPrefix} from "../../utils";
export default {
name: "CmdInputWrapper",
components: {ElFormItem},
props: {
property: Object,
value: {},
container: {},
propertyName: String,
editable: Boolean,
switchable: Boolean,
contentWidth: {
type: String,
default: '65%',
},
labelOffsetTop: {
type: Number,
default: 0,
},
},
data() {
return {
//cmdValue: this.container[this.cmdPropName] || '',
}
},
watch: {
container(v) {
//this.cmdValue = this.container[this.cmdPropName] || '';
},
},
computed: {
cmdValue() {
return this.container[this.cmdPropName] || '';
},
cmdMode() {
return this.container.hasOwnProperty(this.cmdPropName);
},
cmdPropName() {
return cmdPrefix + this.propertyName;
},
cmdOldPropName() {
return cmdOldPrefix + this.propertyName;
},
},
methods: {
onClickLink() {
if (this.cmdMode) {
this.$set(this.container, this.cmdOldPropName, this.cmdValue);
this.$delete(this.container, this.cmdPropName);
} else {
let cmdValue = this.container[this.cmdOldPropName] || '';
this.$set(this.container, this.cmdPropName, cmdValue);
}
},
onInputCmdValue(v) {
this.container[this.cmdPropName] = v;
},
saveCmdValue(v) {
let oldValue = this.container[this.cmdPropName];
//this.cmdValue = v;
this.$set(this.container, this.cmdPropName, v);
this.$emit('cmd-prop-change', v, this.container, this.cmdPropName, oldValue);
},
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property" :propertyName="propertyName" class="color-input-container">
<el-color-picker
:disabled="!editable"
class="picker"
......@@ -8,16 +7,16 @@
show-alpha
:predefine="predefineColors">
</el-color-picker>
</input-wrapper>
</template>
<script>
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
export default {
name: "ColorInput",
components: {InputWrapper,},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
data() {
return {
predefineColors: [
......@@ -60,6 +59,7 @@
}
.picker {
flex: 1;
float: right;
}
</style>
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property"
:propertyName="propertyName">
<dynamic-selector style="flex: 1;" :value="value" @input="onChange"
:editable="editable"
:container="container"
:property="property"
:propertyName="propertyName"/>
</input-wrapper>
</template>
<script>
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
import DynamicSelector from "./DynamicSelector";
export default {
name: "DynamicInput",
components: {DynamicSelector, InputWrapper,},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {DynamicSelector, LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
methods: {
onChange(v){
this.$emit('input', v, this.container, this.propertyName, this.value);
......
......@@ -8,9 +8,12 @@
v-model="popoverVisible"
:disabled="!editable"
>
<div>
<el-radio-group v-model="editValue.type" size="mini" @change="onChange" :disabled="!editable">
<el-radio-button v-for="(item, key) in dataTypes" :label="key" :key="key">{{item}}</el-radio-button>
</el-radio-group>
<el-button circle plain type="danger" icon="el-icon-close" class="micro" style="margin-left: 5px;" @click="popoverVisible=false"/>
</div>
<el-input clearable slot="reference" :value="editValue.value" @input="onInput" @change="onChange"
:readonly="!editable"
:placeholder="defaultValue"/>
......@@ -28,7 +31,7 @@
export default {
name: "DynamicSelector",
components: {},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
props: ['value', 'container', 'property', 'propertyName', 'editable'],
data() {
let dataTypes = this.$t('dataTypes');
return {
......
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property" :propertyName="propertyName">
<el-select :disabled="!editable" :value="editValue" @input="onInput" :placeholder="property.default" class="el-select">
<el-option
v-for="(item, key) in property.enum"
:key="item"
:label="item"
:value="item">
<span>{{item}}</span>
<span class="comment"></span>
:key="key"
:label="typeof item === 'object' ? item.label : item"
:value="typeof item === 'object' ? item.value : item">
<span>{{typeof item === 'object' ? item.label : item}}</span>
<span class="enum-input-comment">{{typeof item === 'object' ? item.value : item}}</span>
</el-option>
</el-select>
</input-wrapper>
</template>
<script>
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
export default {
name: "EnumInput",
components: {InputWrapper,},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
computed: {
editValue() {
return this.value === undefined ? this.property.default : this.value;
......
<template>
<el-form-item class="input-wrapper" :label="propertyName" content-float="right"
:content-width="contentWidth" :labelOffsetTop="labelOffsetTop">
<el-tooltip placement="top" class="label" slot="label" trigger="hover" :open-delay="500" :enterable="false">
<el-tooltip placement="left" class="label" slot="label" trigger="hover" :open-delay="500" :enterable="false">
<div slot="content" class="property-name-popover">
<p>{{$t('Alias')}}{{property.alias}}</p>
<p>{{$t('Name')}}{{propertyName}}</p>
......@@ -18,7 +18,7 @@
<slot></slot>
</template>
<el-popover
v-if="linkable"
v-if="switchable"
trigger="click"
:disabled="!editable"
>
......@@ -42,7 +42,7 @@
import ElFormItem from "./form-item";
export default {
name: "InputWrapper",
name: "LinkableInputWrapper",
components: {ElFormItem},
props: {
property: Object,
......@@ -50,7 +50,7 @@
container: {},
propertyName: String,
editable: Boolean,
linkable: Boolean,
switchable: Boolean,
contentWidth: {
type: String,
default: '65%',
......
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property"
:propertyName="propertyName">
<div style="display: flex;flex: 1;">
<el-popover
popper-class="input-area-popover"
......@@ -33,18 +31,18 @@
<el-button icon="el-icon-delete" @click="onClickClean" :disabled="!editable"></el-button>
</el-button-group>
</div>
</input-wrapper>
</template>
<script>
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
import {getInputDefaultValue} from "../../utils";
import DynamicSelector from "./DynamicSelector";
export default {
name: "MapInput",
components: {DynamicSelector, InputWrapper,},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {DynamicSelector, LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
data() {
let dataTypes = this.$t('dataTypes');
return {
......
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property"
:propertyName="propertyName">
<div style="display: flex;flex: 1;">
<el-popover
placement="top"
placement="bottom"
popper-class="node-select-popover"
class="node-select-container"
trigger="manual"
width="400"
height="500"
v-model="popoverVisible"
@after-enter="onPopoverShow"
>
<div>
<el-input v-model="filterText" prefix-icon="el-icon-search" size="mini" clearable/>
......@@ -18,8 +18,10 @@
v-if="popoverVisible"
:data="behavior_views"
:props="defaultProps"
:expand-on-click-node="false"
:expand-on-click-node="true"
:default-expanded-keys="expandedKeys"
draggable
node-key="uuid"
highlight-current
:default-expand-all="true"
@node-click="handleNodeClick"
......@@ -34,8 +36,8 @@
<div class="bottom-bar">
<div></div>
<div>
<el-button @click="onCancel" plain>Cancel</el-button>
<el-button @click="onConfirm" type="primary">Confirm</el-button>
<el-button @click="onCancel" plain>{{$t('Cancel')}}</el-button>
<el-button @click="onConfirm" type="primary">{{$t('Confirm')}}</el-button>
</div>
</div>
</div>
......@@ -43,7 +45,7 @@
:readonly="!editable">
<template slot="prepend">
<el-tooltip effect="dark" :content="nodePath" placement="top" :open-delay="500" :disabled="!nodePath">
<el-button>{{nodeScheme}}</el-button>
<span>{{nodeScheme}}</span>
</el-tooltip>
</template>
</el-input>
......@@ -53,18 +55,18 @@
<el-button icon="el-icon-delete" @click="onClickClean" :disabled="!editable"></el-button>
</el-button-group>
</div>
</input-wrapper>
</template>
<script>
import {mapGetters} from "vuex";
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
import {nodeScheme} from "../../utils";
export default {
name: "NodeSelectInput",
components: {InputWrapper,},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
data() {
return {
nodeScheme: nodeScheme,
......@@ -76,6 +78,7 @@
label: 'name'
},
nodePath: '',
expandedKeys: [],
}
},
watch: {
......@@ -123,7 +126,12 @@
}
},
filterNodeMethod(value, data) {
if(value.startsWith(':uuid|')){
let targetUUID = value.replace(':uuid|', '');
return data.uuid === targetUUID;
}else{
return data.name.toLowerCase().includes(value.toLowerCase());
}
},
onInput(v, oldValue) {
if (v !== this.value) {
......@@ -135,6 +143,7 @@
},
onClickEdit() {
this.popoverVisible = !this.popoverVisible;
},
onClickClean() {
this.nodePath = '';
......@@ -152,6 +161,16 @@
handleNodeClick(data, node) {
this.selectedNodeUUID = data.uuid;
},
onPopoverShow(){
let uuid = this.editValue;
if(uuid){
this.filterText = ':uuid|' + uuid;
this.updateFilter();
this.$refs.tree.setCurrentKey(uuid);
this.expandedKeys = [uuid];
this.selectedNodeUUID = uuid;
}
},
},
}
</script>
......
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property" :propertyName="propertyName">
<el-input-number :disabled="!editable" :value="editValue" @input="onInput" controls-position="right"
<el-input-number :disabled="!editable" :value="editValue" @change="onInput" controls-position="right"
:placeholder="defaultValue"></el-input-number>
</input-wrapper>
</template>
<script>
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
import {getInputDefaultValue} from "../../utils";
export default {
name: "NumberInput",
components: {InputWrapper,},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
computed: {
editValue() {
return this.value === undefined ? this.property.default : this.value;
......
<template>
<el-form-item class="input-wrapper" :label="propertyName" content-float="right"
:content-width="contentWidth" :labelOffsetTop="labelOffsetTop">
<el-tooltip placement="left" class="label" slot="label" trigger="hover" :open-delay="500" :enterable="false">
<div slot="content" class="property-name-popover">
<p>{{$t('Alias')}}{{property.alias}}</p>
<p>{{$t('Name')}}{{propertyName}}</p>
</div>
<div>
<p class="alias">{{property.alias||propertyName}}</p>
<p class="property-name">{{propertyName}}</p>
</div>
</el-tooltip>
<template>
<slot></slot>
</template>
</el-form-item>
</template>
<script>
import camelcase from 'camelcase'
import ElFormItem from "./form-item";
export default {
name: "SampleInputWrapper",
components: {ElFormItem},
props: {
property: Object,
value: {},
container: {},
propertyName: String,
editable: Boolean,
switchable: Boolean,
contentWidth: {
type: String,
default: '65%',
},
labelOffsetTop: {
type: Number,
default: 0,
},
},
data() {
return {}
},
watch: {
},
computed: {
},
methods: {
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<el-popover
placement="top"
trigger="hover"
width="auto"
:disabled="!legalUrl"
:content="url"
class="source-input"
popper-class="source-input-popover"
>
<div class="wrapper">
<img style="max-width: 200px;" class="thumbnail alpha-image-background" v-if="!url.endsWith('.svga')" :src="url" alt=""/>
<svga-preview-view :max-width="200" class="thumbnail alpha-image-background" :max-height="200" v-if="url.endsWith('.svga')" :src="url" alt=""/>
<span>{{assetSize(url)}}</span>
</div>
<el-input slot="reference" :disabled="!editable" v-model="editValue" controls-position="right" :placeholder="defaultValue"
@change="onChange" @drop.native="drop" @dragover.native="dragOver">
<el-button slot="append" icon="el-icon-aim" @click="locateAsset"></el-button>
</el-input>
</el-popover>
</template>
<script>
import {mapGetters} from 'vuex'
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
import {assetScheme, getInputDefaultValue} from "../../utils";
import events from "@/global-events.js"
import SvgaPreviewView from "../SvgaPreviewView";
export default {
name: "SourceInput",
components: {SvgaPreviewView, LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
data(){
return {
editValue: this.getEditValue(),
}
},
mounted(){
this.editValue = this.getEditValue();
},
watch:{
value(){
this.editValue = this.getEditValue();
}
},
computed: {
defaultValue(){
return getInputDefaultValue(this.property);
},
url: function () {
if (this.value) {
if (this.value.startsWith(assetScheme)) {
let uuid = this.value.replace(assetScheme, '');
let _ass = this.assets.find(a => a.uuid === uuid);
return _ass ? _ass.url : '';
} else {
return this.value;
}
} else {
return '';
}
},
legalUrl: function () {
return this.value && this.value.startsWith(assetScheme);
},
...mapGetters(['assets', 'assetSize']),
},
methods: {
getEditValue() {
return this.value === undefined ? this.property.default : this.value;
},
onChange(v) {
if(v !== this.value){
this.$emit('input', v, this.container, this.propertyName, this.value);
}
},
drop(e) {
if (this.$store.state.project.dragUUID) {
console.log('native drop', this.$store.state);
this.editValue = assetScheme + this.$store.state.project.dragUUID;
this.onChange(this.editValue)
}
},
dragOver(e) {
if(this.editable){
e.preventDefault();
}
},
locateAsset() {
let uuid = this.value ? this.value.replace(assetScheme, '') : null;
if (uuid) {
let asset = this.assets.find(a => a.uuid === uuid);
if (asset) {
events.$emit('select-asset-item', asset);
}
}
},
},
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property" :propertyName="propertyName">
<div style="display: flex;flex: 1;">
<el-popover
placement="top"
......@@ -34,17 +33,17 @@
<el-button icon="el-icon-delete" @click="onClickClean" :disabled="!editable"></el-button>
</el-button-group>
</div>
</input-wrapper>
</template>
<script>
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
import {getInputDefaultValue} from "../../utils";
export default {
name: "StringInput",
components: {InputWrapper,},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
data() {
return {
editValueOrigin: this.value,
......
<template>
<div style="display: flex;flex: 1;">
<el-input :value="value" readonly/>
</div>
</template>
<script>
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
import {getInputDefaultValue} from "../../utils";
export default {
name: "TextInput",
components: {LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
data() {
return {
}
},
computed: {
},
watch: {
},
methods: {
},
}
</script>
<style scoped>
.bottom-bar {
margin-top: 5px;
display: flex;
align-items: center;
justify-content: space-between;
}
</style>
<template>
<input-wrapper :editable="editable" :linkable="linkable" :value="value" :container="container" :property="property"
:propertyName="propertyName">
<div class="vector2-input">
<span class="field-label">x</span>
<el-input-number :disabled="!editable" :value="editValue.x" @input="v=>onInput(v, 'x')" controls-position="right"
<el-input-number style="flex: 1;" :disabled="!editable" v-model="editValue.x" @change="v=>onInput(v, 'x')" controls-position="right"
:placeholder="defaultValue.x"></el-input-number>
<span class="field-label">y</span>
<el-input-number :disabled="!editable" :value="editValue.y" @input="v=>onInput(v, 'y')" controls-position="right"
<el-input-number style="flex: 1;" :disabled="!editable" v-model="editValue.y" @change="v=>onInput(v, 'y')" controls-position="right"
:placeholder="defaultValue.y"></el-input-number>
</input-wrapper>
</div>
</template>
<script>
import {parseVector2} from "props-compute";
import InputWrapper from "./InputWrapper";
import LinkableInputWrapper from "./LinkableInputWrapper";
import CmdInputWrapper from "./CmdInputWrapper";
export default {
name: "Vector2Input",
components: {InputWrapper,},
props: ['value', 'container', 'property', 'propertyName', 'editable', 'linkable'],
components: {LinkableInputWrapper, CmdInputWrapper},
props: ['value', 'container', 'property', 'propertyName', 'editable'],
data() {
return {
editValue: {},
//editValue: {},
}
},
watch: {
value(v) {
let editValue = v === undefined ? this.property.default : v;
this.editValue = parseVector2(editValue);
//let editValue = v === undefined ? this.property.default : v;
//this.editValue = parseVector2(editValue);
}
},
computed: {
/*editValue() {
return this.value === undefined ? this.property.default : this.value;
},*/
editValue() {
return this.value === undefined ? this.defaultValue : parseVector2(this.value);
},
defaultValue() {
let value = this.property.default;
let placeholder = {};
......
......@@ -4,6 +4,7 @@
export {default as NumberInput} from './NumberInput';
export {default as StringInput} from './StringInput';
export {default as TextInput} from './TextInput';
export {default as EnumInput} from './EnumInput';
export {default as BooleanInput} from './BooleanInput';
export {default as ColorInput} from './ColorInput';
......@@ -12,3 +13,4 @@ export {default as NodeSelectInput} from './NodeSelectInput';
export {default as MapInput} from './MapInput';
export {default as DynamicInput} from './DynamicInput';
export {default as Vector2Input} from './Vector2Input';
export {default as SourceInput} from './SourceInput';
......@@ -23,7 +23,7 @@ export const SSO_VERIFY_PAGE_URL = '/sso/logout';
export const DOCK_POINT_OFFSET = 4;
export const PROJECT_PAGE_SIZE = 10;
export const PROJECT_PAGE_SIZE = 20;
export const HISTORY_PAGE_SIZE = 20;
//文件类型图标 t表示展示缩略图
......
......@@ -213,10 +213,19 @@
"Name required": "名字必填",
"Index page exists": "已存在首页",
"Please config projectID": "请先配置{env}环境下的projectId",
"Online ticket is invalid": "线上tickct失效,请联系管理员修改",
"SSO is invalid": "SSO失效,请重新验证",
"Are you sure to transform process?": "你确定将此转化为{inlineType}过程吗?",
"Input custom process name": "请输入自定义过程名称",
"Invalid name": "无效名称",
"Function cmd": "功能指令",
"Edit store": "编辑数据",
"Edit computed": "计算属性",
"Are you sure to delete this cmd?": "确定删除这个指令吗?",
"Store editor": "数据编辑器",
"Store": "数据",
"Operate": "操作",
"Computed": "计算属性",
"Are you sure to close?": "确定关闭吗?",
"eventGroup": {
"in": "接收",
"out": "派发"
......@@ -237,6 +246,7 @@
"pack": {
"label": "打包",
"sub": {
"pack-debug-mode": "调试打包",
"pack-manager": "打包管理"
}
},
......@@ -367,5 +377,9 @@
"rename": "重命名",
"replace": "替换",
"combine": "合并"
},
"customCmds": {
"z-for": "循环",
"z-if": "存在"
}
}
\ No newline at end of file
......@@ -4,15 +4,18 @@
* 环境
*/
import Vue from "vue";
import {envApi} from "../../api";
import {Parser} from "svgaplayerweb";
const storeKey = 'code-sync-serve-config';
const svgaParser = new Parser();
export const editorStore = {
state: {
initialized: false,
name: 'Zeroing Editor',
version: '1.0.3',
version: '0.1.0',
templates: {
builtin: ['blank'],
custom: [],
......@@ -21,7 +24,10 @@ export const editorStore = {
autoLaunch: false,
ip: 'localhost',
port: 7788,
}
},
svgaCache: {},
svgaLoadingQueue: {},
assetSizeCache: {},
},
mutations: {
updateEnv(state, env) {
......@@ -40,6 +46,13 @@ export const editorStore = {
},
},
getters: {
assetSize: state => url => {
let size = state.assetSizeCache[url];
if(size){
return `${size.width}x${size.height}`
}
return '0x0';
}
},
actions: {
async updateEnv({state, commit}) {
......@@ -48,5 +61,67 @@ export const editorStore = {
commit('updateEnv', env);
}
},
loadSvga({state, commit}, url) {
const {svgaCache, svgaLoadingQueue} = state;
return new Promise((resolve, reject) => {
let videoItem = svgaCache[url];
if (videoItem) {
resolve(videoItem);
} else if (svgaLoadingQueue[url]) {
svgaLoadingQueue[url].push({resolve, reject});
} else {
svgaLoadingQueue[url] = [{resolve, reject}];
svgaParser.load(url, (videoItem) => {
let queue = svgaLoadingQueue[url];
svgaCache[url] = videoItem;
while (queue.length > 0) {
let item = queue.shift();
item.resolve(videoItem);
}
delete svgaLoadingQueue[url];
}, (e) => {
let queue = svgaLoadingQueue[url];
while (queue.length > 0) {
let item = queue.shift();
item.reject(e);
}
delete svgaLoadingQueue[url];
}
)
}
})
},
async updateAssetSize({state, commit, dispatch}, url) {
let size = state.assetSizeCache[url];
if (!size) {
let extname = url.substr(url.lastIndexOf('.'));
switch (extname) {
case '.svga':
let data = await dispatch('loadSvga', url);
Vue.set(state.assetSizeCache, url, data.videoSize);
break;
case '.jpg':
case '.png':
case '.gif':
case '.svg':
await new Promise((resolve, reject) => {
let img = new Image();
img.onload = function () {
let size = {
width: img.width,
height: img.height,
};
Vue.set(state.assetSizeCache, url, size);
resolve(size);
};
img.onerror = function (e) {
reject(e);
};
img.src = url;
});
break;
}
}
},
}
};
......@@ -96,6 +96,8 @@ export const projectStore = {
id: '',
name: '',
creator: '',
operator: '',
operators: '',
data: {
options: {},
views: [],
......@@ -246,7 +248,7 @@ export const projectStore = {
if (view.type) {
// view.type有值说明修改了节点的类型
// 如果修改了节点类型,需要删除无用的属性
let defaultProps = Object.keys(getCmpProps(view.type));
let defaultProps = Object.keys(getCmpProps(view.type).props);
_.forIn(newView.properties, (value, key) => {
if (defaultProps.indexOf(key) === -1) {
......@@ -481,7 +483,22 @@ export const projectStore = {
setMockServeEnabled(state, enabled) {
state.mockServeEnabled = enabled;
}
},
modifyViewStore(state, {view, store}){
view.store = store;
this.commit('makeProjectDirty');
},
addCmd(state, cmd){
Vue.set(state.activeComponent.properties, cmd, '');
this.commit('makeProjectDirty');
},
deleteCmd(state, cmd){
Vue.delete(state.activeComponent.properties, cmd);
this.commit('makeProjectDirty');
},
},
getters: {
project(state) {
......@@ -676,15 +693,25 @@ export const projectStore = {
return getTopView(node.parent);
}
};
let _view = getTopView(data.node);
if (!data.fromPlayground) {
// 点击视图区域选中节点
// 则需要切换当前节点所在的最顶层视图
let _view = getTopView(data.node);
if (_view && _view.data) {
context.state.activeViews = _view.data.uuid;
}
}
if(_view.data === data.data){
if(!data.data.hasOwnProperty('$isRootView')){
Object.defineProperty(data.data, '$isRootView', {
get(){
return true;
}
})
}
}
context.commit('activeComponent', data.data);
},
/**
......@@ -809,11 +836,11 @@ export const projectStore = {
return failedList;
},
async packProject({state, dispatch}, params) {
let debug = false;
let debug = params.debug;
let packedAssets;
if (!debug) {
//if (!debug) {
packedAssets = await packAssets(state.data.assets);
}
//}
const packResult = await projectApi.pack(state.id, debug, packedAssets);
console.log(packResult);
......
......@@ -94,6 +94,10 @@ $dock-pin-width: 9px;
stroke: $--color-primary;
stroke-dasharray: 5, 1;
}
&.contact {
stroke: $--color-primary;
}
}
.node {
......@@ -281,7 +285,6 @@ $dock-pin-width: 9px;
flex-direction: column;
.wrapper {
padding: 5px;
flex: 1;
......@@ -302,19 +305,20 @@ $dock-pin-width: 9px;
}
}
.inside-code{
.inside-code {
flex: 1;
height: 0;
border-top: 1px solid $--border-color-base;
.scrollbar {
height: 100%;
.code-body{
.code-body {
tab-size: 1.5em;
width: 100%;
margin: 0;
font-size: 12px;
white-space:pre-wrap;
white-space: pre-wrap;
}
}
}
......@@ -394,7 +398,7 @@ $dock-pin-width: 9px;
}
}
.property-name-popover{
.property-name-popover {
p {
margin: 0;
//text-overflow: ellipsis;
......@@ -414,7 +418,7 @@ $dock-pin-width: 9px;
}
}
.props-editor{
.props-editor {
.linked {
display: block;
height: 26px;
......@@ -455,22 +459,37 @@ $dock-pin-width: 9px;
color: $--color-text-secondary;
}
}
.field-label{
.field-label {
padding: 0 5px;
color: $--color-text-secondary;
}
}
.el-form-item__content {
display: flex;
justify-content: flex-end;
.el-input-number--mini {
.source-input {
flex: 1;
}
.vector2-input {
flex: 1;
display: flex;
width: 0;
}
.node-select-container {
flex: 1;
}
}
.source-input-popover {
.wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
}
.enum-input-comment {
float: right;
color: $--color-text-secondary;
}
......@@ -17,6 +17,7 @@
margin-bottom: 1px;
padding: 5px 10px;
font-size: 13px;
justify-content: space-between;
.menu {
flex: 1;
......@@ -47,6 +48,38 @@
background-color: $--pane-background-color;
}
.package-list{
display: flex;
flex-direction: column;
.list {
display: flex;
flex-direction: column;
flex: 1;
margin-bottom: 5px;
.item + .item {
}
.item {
display: flex;
align-items: center;
padding-top: 5px;
&:hover {
& > .delete-button {
visibility: visible;
}
}
&:nth-child(odd){
background-color: #FAFAFA;
}
}
}
}
.mapping-list {
display: flex;
flex-direction: column;
......@@ -309,6 +342,43 @@
}
}
.store-editor-dialog {
.wrapper {
height: 50vh;
.tabs {
height: 100%;
display: flex;
flex-direction: column;
.el-tabs__content {
flex: 1;
& > div {
height: 100%;
}
.editor {
height: 100%;
}
.computed-editor{
.editor{
height: 200px;
}
}
}
}
}
.dialog-footer{
.save-button{
margin-left: 10px;
}
}
}
.px-publish-view {
display: flex;
flex-direction: column;
......
......@@ -48,6 +48,10 @@
overflow-y: hidden;
}
.pr-10px{
padding-right: 10px;
}
.el-form-item--mini.el-form-item {
margin-bottom: 3px;
}
......@@ -76,8 +80,12 @@
line-height: 30px;
}
.el-collapse-item__content{
padding-bottom: 5px;
}
.el-tabs--border-card > .el-tabs__content {
padding: 5px 0 5px 5px;
padding: 5px;
}
.el-input-number.is-controls-right .el-input__inner {
......@@ -98,3 +106,26 @@
padding: 3px;
align-self: center;
}
.el-tabs__item {
height: 25px;
line-height: 25px;
}
.el-tab-pane {
height: 100%;
}
.el-form-item__content {
display: flex;
//justify-content: flex-end;
.el-input-number--mini {
flex: 1;
}
.el-button{
padding-left: 5px;
padding-right: 5px;
}
}
......@@ -6,45 +6,39 @@
display: flex;
flex-direction: column;
&>:last-child{
& > :last-child {
flex: 1;
height: 0;
}
.el-tabs__item {
height: 25px;
line-height: 25px;
}
.el-tab-pane{
height: 100%;
}
.zero-inspector-props-form {
height: 100%;
.el-form{
padding-right: 10px;
.form {
height: 100%;
display: flex;
flex-direction: column;
}
.el-input-number.el-input-number--mini, .el-select.el-select--mini {
width: 100%;
.top-bar {
padding: 5px;
}
.el-slider.zero-slider {
width: 180px;
.scrollbar {
flex: 1;
margin-right: -5px;
}
.zero-slider .el-slider__runway.show-input {
width: 100px;
.cmd-input {
.el-input__inner {
color: $--color-primary;
}
.zero-slider > .el-slider__input {
width: 60px;
}
.scrollbar{
height: 100%;
.add-item-bar {
padding-top: 10px;
padding-bottom: 6px;
text-align: center;
}
}
......@@ -53,11 +47,11 @@
display: flex;
flex-direction: column;
.add-trigger{
.add-trigger {
align-self: flex-start;
}
.scrollbar{
.scrollbar {
margin-top: 5px;
height: 100%;
}
......@@ -66,7 +60,7 @@
margin-left: 5px;
}
.trigger-list{
.trigger-list {
color: $--color-text-regular;
font-size: 12px;
padding-right: 10px;
......@@ -74,21 +68,22 @@
.trigger-item + .trigger-item {
margin-top: 5px;
}
.trigger-item {
border: 1px solid lightgray;
border-radius: 3px;
padding: 3px;
.el-button{
.el-button {
padding: 3px;
}
.top-bar{
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
.name{
.name {
color: $--color-primary;
font-size: 14px;
font-weight: bold;
......@@ -111,12 +106,12 @@
align-items: center;
margin-top: 2px;
.name-field{
.name-field {
flex: 1;
margin-left: 3px;
}
.missing-behavior{
.missing-behavior {
color: $--color-danger;
}
......
......@@ -161,7 +161,7 @@ function completeSelfProps(component) {
let defaultProps = getCmpProps(component.type);
// 把这些属性格式转换成key: value
defaultProps = _.mapValues(defaultProps, o => (o.value));
defaultProps = _.mapValues(defaultProps.props, o => (o.default));
return _.merge(defaultProps, component.properties);
}
......@@ -320,26 +320,25 @@ export const styles = {
}
}
const cmpPropsCache = {};
const cmpMetaCache = {};
export function getCmpProps(type) {
if (!type) {
return {}
} else {
let cmpProps = cmpPropsCache[type];
let cmpProps = cmpMetaCache[type];
if (cmpProps) {
cmpProps = _.cloneDeep(cmpProps);
} else {
let typeProps = properties[type];
let inherits = [_.cloneDeep(typeProps)];
while (typeProps.base) {
typeProps = cmpPropsCache.hasOwnProperty(typeProps.base) ? cmpPropsCache[typeProps.base] : properties[typeProps.base];
inherits.unshift(_.cloneDeep(typeProps));
let typeMeta = _.cloneDeep(properties[type]);
let inherits = [typeMeta.props];
let tempMeta = typeMeta;
while (tempMeta.base) {
tempMeta = cmpMetaCache.hasOwnProperty(tempMeta.base) ? cmpMetaCache[tempMeta.base] : properties[tempMeta.base];
inherits.unshift(_.cloneDeep(tempMeta.props));
}
let result = Object.assign({}, ...inherits);
cmpProps = cmpPropsCache[type] = result;
delete result.base;
delete result.groupName;
typeMeta.props = Object.assign({}, ...inherits);
cmpProps = cmpMetaCache[type] = typeMeta;
}
return cmpProps;
......
......@@ -32,6 +32,14 @@ export const pxHostMapping = {
prod: "https://activity.m.duiba.com.cn"
};
export const monacoEditorOptions = {
tabSize: 2,
insertSpaces: false
};
export const cmdPrefix = 'z-';
export const cmdOldPrefix = '//z-';
/**
* 节点方案
* @type {string}
......
This diff is collapsed.
......@@ -122,8 +122,8 @@
methods: {
prepare() {
return Promise.all([
this.updateEnv(),
db.open('store'),
this.updateEnv(),
])
},
onKeyPress(e) {
......@@ -234,6 +234,9 @@
case 'pack':
await this.pack();
break;
case 'pack-debug-mode':
await this.pack(true);
break;
case 'pack-manager':
this.$refs.packManagerDialog.show();
break;
......@@ -274,7 +277,7 @@
break;
}
},
async pack() {
async pack(debug) {
let cancel;
cancel = await this.saveProject(false);
if (cancel) {
......@@ -286,6 +289,7 @@
});
try {
await this.packProject({
debug: debug || false,
remark: this.lastSaveRemark,
});
this.$message({
......
......@@ -13,6 +13,10 @@
</div>
<div class="item">
<p>
<span>size</span>:
<span>{{assetSize(currentItem.url)}}</span>
</p>
<p v-for="(field, index) in showFields" :key="index">
<span>{{field}}</span>:
<span>{{currentItem[field]}}</span>
......@@ -57,13 +61,13 @@
}
},
computed: {
...mapGetters(['assets']),
...mapGetters(['assets', 'assetSize']),
},
mounted() {
this.currentItem = null;
events.$on('select-asset-item', this.selectAssetItem);
},
destroyed(){
destroyed() {
events.$off('select-asset-item', this.selectAssetItem);
},
methods: {
......@@ -93,6 +97,7 @@
this.$refs.assetsShow.show(asset);
},
onItemClick(asset) {
this.updateAssetSize(asset.url);
this.currentItem = asset;
},
deleteAll() {
......@@ -105,7 +110,8 @@
}).catch((e) => {
});
},
selectAssetItem(asset){
selectAssetItem(asset) {
this.updateAssetSize(asset.url);
this.currentItem = asset;
this.$refs.assetList.selectItem(asset);
},
......@@ -114,6 +120,7 @@
]),
...mapActions([
'uploadFiles',
'updateAssetSize',
]),
}
}
......
<template>
<div class="file-item" @click="$emit('click', $event)">
<div class="icon">
<i @dragstart="assetDragStart(data)" v-if="!showThumbnail" draggable="true" class="file-icon"
<i @dragstart="assetDragStart(data)" v-if="showIcon" draggable="true" class="file-icon"
:class="fileIcon"></i>
<img @dragstart="assetDragStart(data)" v-if="showThumbnail" draggable="true"
class="thumbnail alpha-image-background" :src="thumbnailUrl"
alt="thumb" @dblclick="onDbclick()">
<svga-preview-view @dragstart="assetDragStart(data)" draggable class="alpha-image-background" v-if="showSvga"
:max-width="60"
:max-height="60"
:src="thumbnailUrl"></svga-preview-view>
<div v-if="editable" class="operate-bar">
<!--<el-button class="micro" circle plain size="mini" type="success" icon="icon-upload" @dblclick.native.stop
@click="onClickReplace"/>-->
......@@ -46,9 +50,11 @@
import {mapMutations, mapActions} from "vuex";
import {fileTypeIcon} from "../../../config";
import {selectFile, strEllipsis} from "../../../utils";
import SvgaPreviewView from "../../../components/SvgaPreviewView";
export default {
name: "FileItem",
components: {SvgaPreviewView},
props: {
data: {type: Object},
editable: {type: Boolean, default: false},
......@@ -68,6 +74,12 @@
showThumbnail() {
return fileTypeIcon[this.data.ext] === 't';
},
showSvga() {
return this.data.ext === '.svga';
},
showIcon() {
return !this.showThumbnail && !this.showSvga;
},
thumbnailUrl() {
return this.data.url;
},
......
<template>
<div class="zero-inspector-props-form" v-if="activeComponent.uuid">
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden">
<el-form ref="form" size="mini" :model="form" label-width="100px" @submit.native.prevent>
<el-form-item label="名称">
<el-form class="form" ref="form" size="mini" :model="form" label-width="100px" @submit.native.prevent>
<div class="top-bar" v-if="activeComponent.$isRootView">
<el-button size="mini" @click="clickEditStoreExp">{{$t('Edit store')}}</el-button>
<el-button size="mini" @click="clickEditStoreComputed">{{$t('Edit computed')}}</el-button>
</div>
<el-form-item :label="$t('Name')">
<el-input v-model="form.name" @input="v => handleChange('name', v)"></el-input>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="form.type" @change="v => handleChange('type', v)" placeholder="请选择类型">
<el-form-item :label="$t('Type')">
<el-select style="flex:1;" v-model="form.type" @change="v => handleChange('type', v)" placeholder="请选择类型">
<el-option v-for="(cmp, key) in componentsMap" :key="key" :label="cmp" :value="key"></el-option>
</el-select>
</el-form-item>
<template v-for="(p, key) in cmpProps">
<el-form-item v-if="key !== 'groupName'" :id="activeComponent.uuid + '-' + key" :key="activeComponent.uuid + key" :label="p.title">
<!-- {{key}} -->
<el-tooltip :disabled="!p.desc" placement="top-start">
<div slot="content">{{p.desc}}</div>
<div>
<dynamic-component :component-value="getPropValue(p, key)" :component-props="getPropProps(p)" :component-type="getPropCmpType(p)" @onChange="v => handlePropertiesChange(key, v)"></dynamic-component>
</div>
</el-tooltip>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="pr-10px">
<el-collapse v-model="activeNames">
<el-collapse-item :title="$t('Props')" name="props">
<props-editor
ref="propsEditor"
:editable="true"
:switchable="true"
mode="cmd"
:data="form.properties"
:meta="propsMeta"
@input="onInput"
/>
</el-collapse-item>
<el-collapse-item :title="$t('Function cmd') + `(${customCmdKeys.length})`" name="cmd">
<el-form-item v-for="key in customCmdKeys" :label="customCmdNames[key]" :key="key" label-width="50px">
<el-input class="cmd-input" v-model="activeComponent.properties[key]" @change="makeProjectDirty"/>
<el-popconfirm :title="$t('Are you sure to delete this cmd?')" @onConfirm="deleteCmd(key)">
<el-button slot="reference" plain icon="el-icon-delete"/>
</el-popconfirm>
</el-form-item>
</template>
</el-form>
<div class="add-item-bar">
<el-dropdown trigger="click" @command="addCmd" placement="bottom" size="small">
<el-button size="mini" type="primary" plain>{{$t('Add')}}</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="(cmd, key) of customCmdNames"
:command="key"
:key="key"
:disabled="customCmdKeys.includes(key)"
>{{cmd}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-collapse-item>
</el-collapse>
</el-scrollbar>
</el-form>
<store-editor-dialog ref="storeEditorDialog"/>
</div>
</template>
<script>
import { mapGetters, mapState } from 'vuex';
import _ from 'lodash';
import { getCmpProps } from '../../../utils/common';
import dynamicComponent from '../components/dynamicComponent';
const componentMapper = {
switch: {
component: 'el-switch',
props: {
width: 100
}
},
select: {
component: 'el-select',
props: {
slotComponent: 'el-option'
}
},
swSelect: {
component: 'sw-select' // 无需传入options
},
source: {
component: 'sw-source'
},
slider: {
component: 'sw-slider'
},
inputNumber: {
component: 'el-input-number',
props: {
size: 'mini',
'controls-position': 'right'
}
},
textArea: {
component: 'el-input',
props: {
type: 'textarea',
autosize: {
minRows: 3,
maxRows: 10
}
}
},
input: {
component: 'el-input'
},
colorPicker: {
component: 'el-color-picker',
props: {
'show-alpha': true
}
}
};
import {mapGetters, mapMutations} from 'vuex';
import PropsEditor from "../../../components/PropsEditor";
import {getCmpProps} from "../../../utils/common";
import StoreEditorDialog from "./PropsTab/StoreEditorDialog";
import ElFormItem from "../../../components/inputs/form-item";
export default {
export default {
name: 'PropsTab',
components: { 'dynamic-component': dynamicComponent },
components: {ElFormItem, StoreEditorDialog, PropsEditor},
data() {
const componentsMap = this.$t('view_node_menu');
const customCmdNames = this.$t('customCmds');
return {
activeNames: 'props',
componentsMap,
customCmdNames,
form: {
name: '',
type: '',
......@@ -98,15 +81,24 @@ export default {
},
computed: {
...mapGetters(['activeComponent', 'activeComponentCopy', 'componentList']),
cmpProps: function() {
// 获取properties.js中的默认配置
propsMeta() {
return getCmpProps(this.activeComponent.type);
},
customCmdKeys() {
let cmdNames = Object.keys(this.customCmdNames);
let keys = [];
for (let key in this.activeComponent.properties) {
if (cmdNames.includes(key)) {
keys.push(key);
}
}
return keys;
}
},
watch: {
activeComponent: {
deep: true,
handler: function(val) {
handler: function (val) {
this.form.name = val.name || '';
this.form.type = val.type || '';
this.form.properties = val.properties || {};
......@@ -119,57 +111,24 @@ export default {
_view[label] = v;
this.$store.dispatch('modifyActiveView', _view);
},
/**
* 基础属性发生改变
*/
handlePropertiesChange(key, v) {
console.log('handlePropertiesChange', key, v);
onInput(value, container, propName, oldValue) {
console.log('propsChange', propName, value);
let _prop = {};
_prop[key] = v;
_prop[propName] = value;
this.$store.dispatch('modifyProperties', _prop);
},
/**
* 获取动态组件的类型
*/
getPropCmpType(item) {
return componentMapper[item.type].component;
},
/**
* 获取动态组件的属性v-bind
*/
getPropProps(item) {
let _cmp = componentMapper[item.type];
return {
size: 'mini',
...(_cmp.props || {}),
...item.props,
options: item.options || {}
};
clickEditStoreExp() {
this.$refs.storeEditorDialog.edit('exp', this.activeComponent);
},
/**
* 获取当前选中节点对应的属性的值
*/
getPropValue(item, key) {
let _properties = this.activeComponentCopy.properties;
return _properties[key] === undefined || _properties[key] === null ? item.value : _properties[key];
clickEditStoreComputed() {
this.$refs.storeEditorDialog.edit('computed', this.activeComponent);
},
...mapMutations(['makeProjectDirty', 'addCmd', 'deleteCmd']),
}
};
};
</script>
<style lang="scss">
.zero-inspector-props-form {
width: 100%;
padding-right: 10px;
.el-divider__text {
background-color: #e9e9e9;
}
.zero-inspector-props-group {
max-height: 500px;
}
}
.script-config-dialog {
height: 350px;
}
</style>
\ No newline at end of file
<template>
<el-dialog
:title="$t('Store editor')"
width="80vw"
:visible.sync="visible"
:append-to-body="true"
:close-on-click-modal="false"
:show-close="false"
:close-on-press-escape="false"
custom-class="store-editor-dialog"
>
<div class="wrapper">
<el-tabs v-model="tab" type="border-card" class="tabs">
<el-tab-pane :label="$t('Store')" name="exp">
<monaco-editor
v-if="showEditor && tab === 'exp'"
class="editor"
v-model="store.exp"
language="javascript"
:options="monacoEditorOptions"
/>
</el-tab-pane>
<el-tab-pane :label="$t('Computed')" name="computed">
<el-table class="computed-editor" :data="store.computed" height="100%" stripe size="mini">
<el-table-column type="expand">
<template slot-scope="scope">
<monaco-editor
class="editor"
v-model="scope.row.script"
language="javascript"
:options="monacoEditorOptions"
/>
</template>
</el-table-column>
<el-table-column
prop="name"
:label="$t('Name')">
<template slot-scope="scope">
<el-input v-model="scope.row.name" size="mini"/>
</template>
</el-table-column>
<el-table-column
width="50"
:label="$t('Operate')">
<template slot-scope="scope">
<el-button circle size="mini" type="danger" icon="el-icon-delete" plain
@click="toDeleteItem(scope.$index)"/>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</div>
<div slot="footer" class="dialog-footer">
<div>
<el-button v-show="tab === 'computed'" size="mini" @click="onAdd">{{$t('Add')}}</el-button>
</div>
<div>
<el-popconfirm @onConfirm="clickClose" placement="top"
:title="$t('Are you sure to close?')">
<el-button slot="reference" size="mini">{{ $t("Close") }}</el-button>
</el-popconfirm>
<el-button class="save-button" type="primary" size="mini" @click="clickSave">{{ $t("Save") }}
</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import {mapMutations} from 'vuex';
import MonacoEditor from "vue-monaco";
import {clonePureObj, monacoEditorOptions} from "../../../../utils";
const storeExp = `return {
}
`;
export default {
name: "StoreEditorDialog",
components: {MonacoEditor},
data() {
return {
visible: false,
tab: 'exp',
monacoEditorOptions,
showEditor: false,
store: {},
}
},
computed: {
},
methods: {
async edit(type, view) {
this.view = view;
console.log(view.storeExp);
this.store = view.store ? clonePureObj(view.store) : {
exp: storeExp,
computed: [],
};
this.visible = true;
this.$nextTick(()=>{
this.showEditor = true;
this.tab = type;
});
},
onAdd() {
this.store.computed.push({
name: '',
script: '',
});
},
onDelete(index) {
this.store.computed.splice(index, 1);
},
toDeleteItem(index) {
this.$confirm(this.$t('Are you sure to delete this item'), this.$t('Alert'), {
confirmButtonText: this.$t('Confirm'),
cancelButtonText: this.$t('Cancel'),
type: 'warning'
}).then(() => {
this.onDelete(index);
}).catch((e) => {
});
},
clickSave() {
this.modifyViewStore({
view: this.view,
store: this.store,
});
this.visible = false;
},
clickClose() {
this.visible = false;
},
...mapMutations(['modifyViewStore'])
}
}
</script>
<style scoped>
</style>
\ No newline at end of file
<template>
<div class="scripts-config-dialog"></div>
</template>
<script>
export default {
}
</script>
<style lang="scss">
.scripts-config-dialog{
position: absolute;
width: 100%;
}
</style>
<template>
<div class="zero-inspector-props-form" v-if="activeComponent.uuid">
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden">
<el-form ref="form" size="mini" :model="form" label-width="100px" @submit.native.prevent>
<el-form class="form" ref="form" size="mini" :model="form" label-width="100px" @submit.native.prevent>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="pr-10px">
<el-collapse accordion v-if="activeComponent.scripts && activeComponent.scripts.length">
<template v-for="(script, index) in activeComponent.scripts">
<el-collapse-item :title="getScriptName(script.script)" :key="script + index">
<template v-for="(p, key) in getScriptOptions(script.script)">
<el-form-item :key="activeComponent.uuid + index + key" :label="p.alias|| key">
<dynamic-component :component-value="getScriptValue(p, key, index)" :component-props="getScriptProps(p, index)" :component-type="getScriptType(p, index)" @onChange="v => handleScriptChange(index, key, v)"></dynamic-component>
</el-form-item>
</template>
<props-editor
ref="propsEditor"
:editable="true"
:data="script.props"
:meta="getScriptOptions(script.script)"
@input="(value, container, propName)=>onInput(index, value, propName)"
/>
<el-form-item label="">
<el-button @click="deleteNodeScript(index)">删除</el-button>
<el-button @click="deleteNodeScript(index)" type="danger" plain>删除</el-button>
</el-form-item>
</el-collapse-item>
</template>
</el-collapse>
<div style="padding-top: 15px;text-align: center;">
<el-dropdown trigger="click" @command="handleAddScript" placement="top" size="small">
<el-button size="mini">{{$t('Add')}}</el-button>
<div class="add-item-bar">
<el-dropdown trigger="click" @command="handleAddScript" placement="bottom" size="small">
<el-button size="mini" type="primary" plain>{{$t('Add')}}</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="(script, key) of scriptPackages" :command="script.id" :key="key">{{script.name}}</el-dropdown-item>
<el-dropdown-item v-for="(script, key) of scriptPackages" :command="script.id" :key="key">
{{script.name}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-form>
</el-scrollbar>
</el-form>
</div>
</template>
<script>
import { mapGetters, mapState } from 'vuex';
import _ from 'lodash';
import dynamicComponent from '../components/dynamicComponent';
import {mapGetters, mapState} from 'vuex';
import _ from 'lodash';
import PropsEditor from "../../../components/PropsEditor";
const scriptTypeMap = {
number: {
component: 'el-input-number',
props: {
size: 'mini'
}
},
string: {
component: 'el-input'
},
boolean: {
component: 'el-switch',
props: {
width: 40
}
},
color: {
component: 'el-color-picker',
props: {
'show-alpha': true
}
},
select: {
component: 'el-select',
props: {
slotComponent: 'el-option'
}
}
};
export default {
export default {
name: 'ScriptsTab',
components: { 'dynamic-component': dynamicComponent },
components: {PropsEditor},
data() {
return {
form: {
......@@ -77,9 +51,7 @@ export default {
},
computed: {
...mapGetters(['activeComponent', 'activeComponentCopy', 'componentList', 'scriptPackages']),
...mapState({
}),
...mapState({}),
},
methods: {
/**
......@@ -92,7 +64,7 @@ export default {
/**
* 事件属性发生改变
*/
handleScriptChange(index, key, v) {
onInput(index, v, key) {
let _props = {};
_props[key] = v;
let _scripts = _.cloneDeep(this.activeComponent.scripts);
......@@ -143,7 +115,7 @@ export default {
},
getScriptOptions(id) {
let _script = this.scriptPackages[id];
return _script ? _script.props : {};
return _script ? _script : {};
},
/**
* 删除节点脚本
......@@ -159,21 +131,9 @@ export default {
});
}
}
};
};
</script>
<style lang="scss">
.zero-inspector-props-form {
width: 100%;
padding-right: 10px;
.el-divider__text {
background-color: #e9e9e9;
}
.zero-inspector-props-group {
max-height: 500px;
}
}
.script-config-dialog {
height: 350px;
}
</style>
\ No newline at end of file
<template>
<div class="tool-bar">
<sample-menu :data="menu" @click-menu="clickMenu"/>
<div style="flex: 1"></div>
<div class="right-part">
<span>
[{{project.name}}]
......
......@@ -28,7 +28,7 @@
ref="tree"
:data="views"
:props="defaultProps"
:expand-on-click-node="false"
:expand-on-click-node="true"
:default-expanded-keys="expandedKeys"
draggable
node-key="uuid"
......@@ -43,6 +43,7 @@
>
<div slot-scope="{ node, data }" class="tree-node">
<div class="node-name" >
<i v-show="Object.keys(iconMap).includes(data.type)" :class="iconMap[data.type]" style="color:#9b82e3"></i>
{{data.name}}
</div>
......@@ -92,6 +93,10 @@
},
expandedKeys: [],
nodeFilterPresets: this.$t('nodeFilterPresets'),
iconMap: {
image: 'el-icon-picture',
label: 'el-icon-edit-outline'
}
};
},
mounted() {
......@@ -217,6 +222,8 @@
* 点击左侧视图列表
*/
handleNodeClick(data, node) {
console.log('handleNodeClick');
this.$store.dispatch('activeComponent', {data, node});
},
toAddView() {
......
......@@ -434,12 +434,43 @@
let hint = 'pn_' + process.uuid === key;
processNode.setActive(hint);
if (hint && this.selectedProcessNode !== processNode) {
// 相关联的线点亮
this.setContactLine(process);
this.selectedProcessNode = processNode;
this.$emit('select-process-node', process);
}
}
}
}
},
setContactLine(process) {
let preArr = [];
let _fetchNode = (_uuid, type) => {
Object.keys(this.lines).filter(_sliceKey => !preArr.includes(_sliceKey)).map(key => {
let itemLine = this.lines[key];
if(type === 'pre') {
if(itemLine.next && itemLine.next.uuid === _uuid) {
preArr.push(key)
if(itemLine.prev && itemLine.prev.uuid) {
_fetchNode(itemLine.prev.uuid, 'pre')
}
}
} else if(type === 'next'){
if(itemLine.prev && itemLine.prev.uuid === _uuid) {
preArr.push(key)
if(itemLine.next && itemLine.next.uuid) {
_fetchNode(itemLine.next.uuid, 'next')
}
}
}
})
}
_fetchNode(process._data.uuid, 'pre');
_fetchNode(process._data.uuid, 'next');
Object.keys(this.lines).map(key => {
this.$set(this.lines, key, { ...this.lines[key], onContact: preArr.includes(key)})
})
},
updateProcessNode(metaID) {
this.$nextTick(() => {
......
<template>
<path class="line" :d="transPath" @dblclick="onDblClick"></path>
<path :class="['line', {'contact': onContact}]" :d="transPath" @dblclick="onDblClick"></path>
</template>
<script>
......@@ -8,12 +8,15 @@
export default {
name: "LinkLine",
props: ['data'],
created() {
this.onContact = false
},
mounted() {
console.log();
},
watch: {
'data': function (v) {
this.onContact = v.onContact;
}
},
computed: {
......
......@@ -59,6 +59,8 @@
@mousedown="onPinDown"
></dock-pin>
</div>
<!-- 判断是否有子节点 -->
<i class="sub-mark el-icon-s-tools" v-show="meta.sub && meta.from === 'custom' && Object.keys(meta.sub).length > 0" style="color:#9b82e3;text-align: center;"></i>
</div>
</foreignObject>
</template>
......
......@@ -4,13 +4,14 @@
ref="editor"
v-model="meta.script"
language="javascript"
:options="monacoConfig"
:options="monacoEditorOptions"
@editorWillMount="editorWillMount"
/>
</template>
<script>
import MonacoEditor from "vue-monaco";
import {monacoEditorOptions} from "../../../utils";
const typesUrl = [
"http://yun.duiba.com.cn/editor/zeroing/types/types.v2.d.ts",
......@@ -36,7 +37,7 @@ $ENV_PROPS$
props: ['meta'],
data() {
return {
monacoConfig: {},
monacoEditorOptions,
}
},
mounted() {
......
......@@ -5,7 +5,7 @@
<template slot="prepend">{{$t('Name')}}</template>
</el-input>
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="scrollbar-view">
<props-editor ref="propsEditor" :editable="editable" :linkable="true" :data="process.data" :meta="process.meta"/>
<props-editor ref="propsEditor" :editable="editable" :switchable="true" mode="linkable" :data="process.data.props" :meta="process.meta"/>
</el-scrollbar>
<div class="inside-code">
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden" view-class="scrollbar-view">
......
<template>
<component v-if="cmpValue !== null" :is="componentType" v-model="cmpValue" v-bind="componentProps" @change="v => handleChange(v)" @input="v=>handleInput(v)">
<d-slot
v-for="oitem in componentProps.options"
:is="componentProps.slotComponent"
:key="oitem.value"
:label="oitem.label || oitem.value"
:value="oitem.value"
>
</d-slot>
</component>
</template>
<script>
import swSlider from '../../../components/customSettings/slider';
import swSelect from '../../../components/customSettings/selector';
import swSource from '../../../components/customSettings/source';
export default {
components: {
swSlider,
swSelect,
swSource
},
props: {
componentValue: [String, Number, Boolean],
componentType: {
type: String,
default: ''
},
componentProps: {
type: Object,
default: () => {}
}
},
data() {
return {
cmpValue: null
};
},
mounted() {
this.cmpValue = this.componentValue;
},
watch: {
componentValue: function(val) {
this.cmpValue = val;
}
},
methods: {
/**
* 组件值发生改变
*/
handleChange(v) {
// console.log('handleChange', v);
this.$emit('onChange', v);
// let _prop = {};
// _prop[this.label] = v;
// this.$store.dispatch('modifyProperties', _prop);
},
handleInput(v) {
if (this.componentType === 'el-input') {
this.handleChange(v);
}
}
}
};
</script>
......@@ -55,13 +55,13 @@ export default {
},
position() {
let _props = this.activeComponentCopy.properties || {};
const _node = properties.node;
const props = properties.node.props;
// console.log('********', _props);
let result = {
x: _props.x || _node.x.value,
y: _props.y || _node.y.value,
w: _props.width || _node.width.value,
h: _props.height || _node.height.value
x: _props.x || props.x.default,
y: _props.y || props.y.default,
w: _props.width || props.width.default,
h: _props.height || props.height.default,
};
// console.log('####position', result);
......
......@@ -127,10 +127,10 @@ export default {
const _node = properties.node;
// console.log('********', _props);
let result = {
x: _props.x || _node.x.value,
y: _props.y || _node.y.value,
w: _props.width || _node.width.value,
h: _props.height || _node.height.value
x: _props.x || _node.x.default,
y: _props.y || _node.y.default,
w: _props.width || _node.width.default,
h: _props.height || _node.height.default
};
console.log('####position', result);
......
......@@ -18,7 +18,7 @@
class="editor"
v-model="props.row.data"
language="json"
:options="monacoConfig"
:options="monacoEditorOptions"
/>
</template>
</el-table-column>
......@@ -84,7 +84,7 @@
<script>
import {mapState, mapMutations, mapActions} from 'vuex'
import EnabledSetter from "../components/EnabledSetter";
import {clonePureObj} from "../../../utils";
import {clonePureObj, monacoEditorOptions} from "../../../utils";
import MonacoEditor from "vue-monaco";
export default {
......@@ -94,9 +94,7 @@
return {
visible: false,
mocks: [],
monacoConfig: {
}
monacoEditorOptions,
}
},
mounted() {
......
<template>
<el-dialog :title="$t('Process Search')" width="80%" :visible.sync="visible"
:append-to-body="true"
:close-on-click-modal="false"
custom-class="meta-search-dialog"
>
<div class="wrapper">
......
......@@ -55,10 +55,8 @@
import {mapGetters} from "vuex";
import copy from "copy-to-clipboard";
import {
getProjectSkins,
saveSkins,
getTestEnvTicket,
getProdTicket,
getSkins,
saveSkin,
sendDingTalk
} from "../../../../src/api/polaris";
import {pxHostMapping} from "../../../utils";
......@@ -109,12 +107,6 @@
async getSkinsList() {
const {env} = this;
if (env === "test") {
await this.getTicket(getTestEnvTicket);
} else if (env === "prod") {
await this.getTicket(getProdTicket, "prod_ticket");
}
const {pxEnv} = this.options;
const pxPid = this.pxPid = pxEnv[env];
......@@ -126,7 +118,7 @@
return;
}
try {
const data = await getProjectSkins(pxPid, env);
const data = await getSkins(pxPid, env);
if (data.success !== false) {
this.pxProjectName = data.name;
this.skins = data.skins;
......@@ -134,7 +126,6 @@
// 如果是测试环境,ticket过期,则重新获取ticket
if (env === "test" && data.notLogin) {
localStorage.setItem("ticket", "");
await this.getTicket(getTestEnvTicket);
this.getSkinsList(this.pxPid);
} else if (env === "prod" && data.notLogin) {
localStorage.setItem("prod_ticket", "");
......@@ -194,10 +185,7 @@
name,
env: env
};
this.$refs.SelectQADialog.show();
return;
const {ret} = await saveSkins(data);
const {ret} = await saveSkin(data);
if (ret.success !== false) {
this.$message({
message: `操作成功`,
......@@ -233,13 +221,6 @@
type: "success"
});
},
async getTicket(fn, name = "ticket") {
let ticket = localStorage.getItem(name);
if (!ticket) {
ticket = await fn();
localStorage.setItem(name, ticket);
}
},
/*getTpl(id) {
const {skins} = this;
let tpl = "";
......
......@@ -19,6 +19,7 @@
class="editor"
v-model="skin.html"
language="html"
:options="monacoEditorOptions"
/>
</el-form-item>
</el-form>
......@@ -49,7 +50,7 @@
<script>
import MonacoEditor from "vue-monaco";
import {clonePureObj} from "../../../utils";
import {clonePureObj, monacoEditorOptions} from "../../../utils";
import ElFormItem from "../../../components/inputs/form-item";
export default {
......@@ -63,6 +64,7 @@
pxProjectName: '',
skin: null,
operate: 0,
monacoEditorOptions,
rules: {
name: [
{required: true, message: this.$t('Skin name required'), trigger: 'blur'},
......
......@@ -89,7 +89,7 @@
import EnabledSetter from "../components/EnabledSetter";
import MonacoEditor from "vue-monaco";
import PxConfigPopover from "../components/PxConfigPopover";
import {getProdTicket, getProjectSkins, getTestEnvTicket, saveSkins} from "../../../api/polaris";
import {getSkins, saveSkin} from "../../../api/polaris";
import {playWaiting, pxHostMapping} from "../../../utils";
import copy from "copy-to-clipboard";
import PxSkinEditor from "./PxSkinEditor";
......@@ -171,12 +171,6 @@
async getSkinsList() {
const {env} = this;
if (env === "test") {
await this.getTicket(getTestEnvTicket);
} else if (env === "prod") {
await this.getTicket(getProdTicket, "prod_ticket");
}
const {pxEnv} = this.options;
const pxPid = this.pxPid = pxEnv[env];
......@@ -188,45 +182,19 @@
return;
}
try {
const data = await getProjectSkins(pxPid, env);
if (data.success !== false) {
const data = await getSkins(pxPid, env);
this.pxProjectName = data.name;
this.skins = data.skins;
} else {
// 如果是测试环境,ticket过期,则重新获取ticket
if (env === "test" && data.notLogin) {
localStorage.setItem("ticket", "");
await this.getTicket(getTestEnvTicket);
this.getSkinsList(this.projectId);
} else if (env === "prod" && data.notLogin) {
localStorage.setItem("prod_ticket", "");
this.$message({
message: this.$t('Online ticket is invalid'),
type: "info"
});
this.pxProjectName = '';
this.skins = [];
} else {
this.$message({
message: data.message,
type: "error"
});
this.pxProjectName = '';
this.skins = [];
}
}
} catch (e) {
this.pxProjectName = '';
this.skins = [];
throw new Error(e.message);
this.invalidTicketNotice();
}
},
async getTicket(fn, name = "ticket") {
let ticket = localStorage.getItem(name);
if (!ticket) {
ticket = await fn();
localStorage.setItem(name, ticket);
}
async invalidTicketNotice(){
const {env} = this;
this.pxProjectName = '';
this.skins = [];
await this.$alert(this.$t('SSO is invalid'));
window.open('/sso/login?env=' + env, '_blank');
},
async saveSkin(skin, operate) {
const {
......@@ -234,16 +202,18 @@
pxPid,
} = this;
const {id, html, name, type} = skin;
const data = {
const params = {
projectId: pxPid,
type: (operate + 1).toString(),
currentHtmlType: type,
skinId: id,
tpl: html,
operate: operate + 1,
type,
id,
html,
name,
env,
isProd: true,
qas: [921],
};
const {ret} = await playWaiting(saveSkins(data), this.$t('In processing'));
const {ret} = await playWaiting(saveSkin(params), this.$t('In processing'));
if (ret.success !== false) {
this.$message({
......
......@@ -10,7 +10,7 @@
<div class="mapping-list">
<div class="list" v-if="customMeta">
<div class="item" v-for="(item, index) in customMeta.assets" :key="index">
<span class="name">{{item.name}}</span>
<span class="name">{{item.name}}({{assetSize(item.url)}})</span>
<div class="diff">
<div class="side left">
<span class="extname">
......@@ -18,6 +18,10 @@
</span>
<div class="thumbnail">
<img class="alpha-image-background" v-if="showThumbnail(item.ext)" :src="item.url">
<svga-preview-view class="alpha-image-background" v-if="item.ext === '.svga'"
:max-width="100"
:max-height="100"
:src="item.url"></svga-preview-view>
</div>
<el-input size="mini" v-model="item.url" readonly/>
</div>
......@@ -28,7 +32,11 @@
</span>-->
<div class="thumbnail">
<img class="alpha-image-background" v-if="item.replace && showThumbnail(item.ext)"
:src="resolveImg(item)">
:src="resolveSrc(item)">
<svga-preview-view class="alpha-image-background" v-if="item.replace && item.ext === '.svga'"
:max-width="100"
:max-height="100"
:src="resolveSrc(item)"></svga-preview-view>
</div>
<el-input size="mini" v-model="item.replace" clearable>
<el-button slot="append" icon="el-icon-brush" @click="onClickReplace(item)"/>
......@@ -51,16 +59,17 @@
</template>
<script>
import {mapMutations, mapState, mapGetters} from 'vuex';
import {mapMutations, mapState, mapGetters, mapActions} from 'vuex';
import {fileTypeIcon} from "../../../../config";
import SelectAssetDialog from "../SelectAssetDialog";
import {clonePureObj} from "../../../../utils";
import SvgaPreviewView from "../../../../components/SvgaPreviewView";
const linkScheme = 'link://';
export default {
name: "AssetMappingEditorDialog",
components: {SelectAssetDialog},
components: {SvgaPreviewView, SelectAssetDialog},
data() {
return {
visible: false,
......@@ -75,13 +84,14 @@
...mapGetters([
'assets',
'customPackages',
'assetSize',
])
},
methods: {
showThumbnail(ext) {
return fileTypeIcon[ext] === 't';
},
resolveImg(item) {
resolveSrc(item) {
let url;
const replace = item.replace;
if (replace.startsWith(linkScheme)) {
......@@ -107,6 +117,9 @@
show(data) {
this.mid = data.id;
this.customMeta = clonePureObj(this.customPackages[this.mid]);
for(let asset of this.customMeta.assets){
this.updateAssetSize(asset.url);
}
for (let uuid in data.assetMapping) {
let asset = this.customMeta.assets.find(item => item.uuid === uuid);
......@@ -135,6 +148,7 @@
},
...mapMutations([]),
...mapActions(['updateAssetSize']),
}
}
</script>
......@@ -153,11 +167,11 @@
flex-direction: column;
align-items: stretch;
.name{
.name {
text-align: center;
}
.diff{
.diff {
position: relative;
border: 1px solid lightgray;
border-radius: 5px;
......
......@@ -8,7 +8,7 @@
<el-scrollbar class="scrollbar" wrap-class="wrap-x-hidden"
view-class="scrollbar-view">
<props-editor v-if="data" ref="propsEditor" :data="data" :meta="meta" label-width="180px" :linkable="false"/>
<props-editor v-if="data" ref="propsEditor" :data="data.props" :meta="meta" label-width="180px"/>
</el-scrollbar>
<div slot="footer" class="dialog-footer">
<div></div>
......
......@@ -3,7 +3,7 @@
<el-tabs style="flex: 1;height: 0;" class="tabs" v-model="activeName">
<el-tab-pane v-for="(group, key, index) in packageInfos" :label="$t('dependenciesTypes')[key]" :name="key" :key="key">
<el-scrollbar v-if="editData" class="scrollbar" wrap-class="wrap-x-hidden" view-class="view package-manager">
<div class="mapping-list">
<div class="package-list">
<div class="list">
<div class="item" v-for="packageInfo in group" :key="packageInfo.id">
<div style="flex: 1;">
......
......@@ -49,7 +49,7 @@
class="editor"
v-model="editData.tpl"
language="html"
:options="monacoConfig"
:options="monacoEditorOptions"
/>
</el-form-item>
</el-form>
......@@ -58,7 +58,7 @@
<script>
import {mapState, mapGetters, mapMutations} from 'vuex';
import {clonePureObj} from "../../../../utils";
import {clonePureObj, monacoEditorOptions} from "../../../../utils";
import MonacoEditor from "vue-monaco";
export default {
......@@ -72,9 +72,7 @@
editData: null,
scaleMode,
rendererType,
monacoConfig: {
},
monacoEditorOptions,
}
},
computed: {
......
......@@ -92,7 +92,6 @@
components: {ElFormItem, ProjectHistoryDialog, DuplicateProjectDialog, CreateProjectDialog},
data() {
return {
appVersion: 'v1.0.0',
currentPage: 1,
pageSize: PROJECT_PAGE_SIZE,
searchWord: '',
......
......@@ -6780,7 +6780,7 @@ promise-inflight@^1.0.1:
"props-compute@http://gitlab2.dui88.com/laoqifeng/props-compute.git":
version "1.0.0"
resolved "http://gitlab2.dui88.com/laoqifeng/props-compute.git#c676e9e12a06ccc3556101197171ee47e9ebdd3c"
resolved "http://gitlab2.dui88.com/laoqifeng/props-compute.git#9c6abcccf41f8d38d3a327716060f5e7722c7c1a"
proxy-addr@~2.0.5:
version "2.0.5"
......@@ -8102,6 +8102,11 @@ svg-tags@^1.0.0:
resolved "https://registry.npm.taobao.org/svg-tags/download/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
svgaplayerweb@^2.3.0:
version "2.3.0"
resolved "https://registry.npmjs.org/svgaplayerweb/-/svgaplayerweb-2.3.0.tgz#417e2620e68f9ba616037a0feeb93354cb63d545"
integrity sha512-o4hcgA3mW0rmt8n9/z8NXrPGhPnmLndKOLoGAVL+nd6S+aOIojDpnZ4nK3PAw9F4xrOA06FqI3VkEsVHVLNYVA==
svgo@^1.0.0:
version "1.3.2"
resolved "https://registry.npm.taobao.org/svgo/download/svgo-1.3.2.tgz?cache=0&sync_timestamp=1572433263159&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsvgo%2Fdownload%2Fsvgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
......
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