mirror of
https://github.com/sahadev/vue-component-creater-ui.git
synced 2025-06-07 05:38:30 +08:00
update: 集成好vuedraggable
This commit is contained in:
parent
1c61f4b83a
commit
b870e28608
27
package-lock.json
generated
27
package-lock.json
generated
@ -2189,6 +2189,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@vue/devtools-api": {
|
||||||
|
"version": "6.0.0-beta.20.1",
|
||||||
|
"resolved": "https://rg.cnpmjs.org/@vue/devtools-api/download/@vue/devtools-api-6.0.0-beta.20.1.tgz",
|
||||||
|
"integrity": "sha512-R2rfiRY+kZugzWh9ZyITaovx+jpU4vgivAEAiz80kvh3yviiTU3CBuGuyWpSwGz9/C7TkSWVM/FtQRGlZ16n8Q=="
|
||||||
|
},
|
||||||
"@vue/eslint-config-airbnb": {
|
"@vue/eslint-config-airbnb": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://r.cnpmjs.org/@vue/eslint-config-airbnb/download/@vue/eslint-config-airbnb-5.3.0.tgz",
|
"resolved": "https://r.cnpmjs.org/@vue/eslint-config-airbnb/download/@vue/eslint-config-airbnb-5.3.0.tgz",
|
||||||
@ -11934,6 +11939,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sortablejs": {
|
||||||
|
"version": "1.14.0",
|
||||||
|
"resolved": "https://rg.cnpmjs.org/sortablejs/download/sortablejs-1.14.0.tgz",
|
||||||
|
"integrity": "sha1-bS4XzL2yX0ZHNN9iHU811Ks1s9g="
|
||||||
|
},
|
||||||
"source-list-map": {
|
"source-list-map": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://r.cnpmjs.org/source-list-map/download/source-list-map-2.0.1.tgz",
|
"resolved": "https://r.cnpmjs.org/source-list-map/download/source-list-map-2.0.1.tgz",
|
||||||
@ -13347,10 +13357,21 @@
|
|||||||
"integrity": "sha1-HuO8mhbsv1EYvjNLsV+cRvgvWCU=",
|
"integrity": "sha1-HuO8mhbsv1EYvjNLsV+cRvgvWCU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"vuedraggable": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://rg.cnpmjs.org/vuedraggable/download/vuedraggable-4.1.0.tgz",
|
||||||
|
"integrity": "sha1-7ezmituKTZ4GrM/538kEDmaFInA=",
|
||||||
|
"requires": {
|
||||||
|
"sortablejs": "1.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"vuex": {
|
"vuex": {
|
||||||
"version": "3.6.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://r.cnpmjs.org/vuex/download/vuex-3.6.2.tgz",
|
"resolved": "https://rg.cnpmjs.org/vuex/download/vuex-4.0.2.tgz",
|
||||||
"integrity": "sha1-I2vAhqhww655lG8QfxbeWdWJXnE="
|
"integrity": "sha1-+Jbb1b8qDpY/AMZ+m2EN50nMrMk=",
|
||||||
|
"requires": {
|
||||||
|
"@vue/devtools-api": "^6.0.0-beta.11"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"watchpack": {
|
"watchpack": {
|
||||||
"version": "1.7.5",
|
"version": "1.7.5",
|
||||||
|
@ -62,7 +62,8 @@
|
|||||||
"vue": "^3.2.22",
|
"vue": "^3.2.22",
|
||||||
"vue-nestable": "^2.6.0",
|
"vue-nestable": "^2.6.0",
|
||||||
"vue-router": "^3.4.9",
|
"vue-router": "^3.4.9",
|
||||||
"vuex": "^3.1.2"
|
"vuedraggable": "^4.1.0",
|
||||||
|
"vuex": "^4.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/generator": "^7.11.6",
|
"@babel/generator": "^7.11.6",
|
||||||
|
@ -16,8 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<attribute-input :enableRemoveButton="true" class="attribute" @save="onSaveAttr" @remove="onRemove"
|
<attribute-input :enableRemoveButton="true" class="attribute" @save="onSaveAttr" @remove="onRemove"
|
||||||
ref="attributeInput" shortcutInitMode="hand" @codeRefresh="generateVueCode"
|
ref="attributeInput" shortcutInitMode="hand" :__rawVueInfo__="currentEditRawInfo">
|
||||||
:__rawVueInfo__="currentEditRawInfo">
|
|
||||||
</attribute-input>
|
</attribute-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,7 +49,7 @@
|
|||||||
<lc-code :rawCode="code" v-model:codeDialogVisible="codeDialogVisible">
|
<lc-code :rawCode="code" v-model:codeDialogVisible="codeDialogVisible">
|
||||||
</lc-code>
|
</lc-code>
|
||||||
<code-structure @save="onSaveAttr" @remove="onRemove" ref="codeStructure" v-model="structureVisible"
|
<code-structure @save="onSaveAttr" @remove="onRemove" ref="codeStructure" v-model="structureVisible"
|
||||||
@codeRefresh="generateVueCode" @onLevelChange="onLevelChange">
|
@reRender="render">
|
||||||
</code-structure>
|
</code-structure>
|
||||||
<CodeEditor v-model:codeDialogVisible="jsDialogVisible" @saveJSCode="saveJSCode"></CodeEditor>
|
<CodeEditor v-model:codeDialogVisible="jsDialogVisible" @saveJSCode="saveJSCode"></CodeEditor>
|
||||||
<VueEditor v-model:vueDialogVisible="vueDialogVisible" @codeParseSucess="codeParseSucess"></VueEditor>
|
<VueEditor v-model:vueDialogVisible="vueDialogVisible" @codeParseSucess="codeParseSucess"></VueEditor>
|
||||||
@ -220,11 +219,6 @@ export default {
|
|||||||
this.mainPanelProvider.saveAttribute(resultList, lc_id);
|
this.mainPanelProvider.saveAttribute(resultList, lc_id);
|
||||||
},
|
},
|
||||||
|
|
||||||
onLevelChange(removeID, movePath) {
|
|
||||||
this.mainPanelProvider.onLevelChange(removeID, movePath);
|
|
||||||
},
|
|
||||||
|
|
||||||
generateVueCode() { },
|
|
||||||
onRemove({ lc_id }) {
|
onRemove({ lc_id }) {
|
||||||
this.mainPanelProvider.remove(lc_id);
|
this.mainPanelProvider.remove(lc_id);
|
||||||
},
|
},
|
||||||
@ -240,10 +234,20 @@ export default {
|
|||||||
this.mainPanelProvider.saveJSCode(code);
|
this.mainPanelProvider.saveJSCode(code);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二级编辑解析
|
||||||
|
*/
|
||||||
codeParseSucess(vueCodeEntity) {
|
codeParseSucess(vueCodeEntity) {
|
||||||
this.mainPanelProvider.render(vueCodeEntity);
|
this.mainPanelProvider.render(vueCodeEntity);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染指定结构
|
||||||
|
*/
|
||||||
|
render(codeEntity) {
|
||||||
|
this.mainPanelProvider.render(codeEntity);
|
||||||
|
},
|
||||||
|
|
||||||
help() {
|
help() {
|
||||||
window.open('/doc')
|
window.open('/doc')
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-card class="attribute-container">
|
<el-card class="attribute-container">
|
||||||
|
|
||||||
<div style="text-algin: center;">
|
<div style="text-align: center;">
|
||||||
<el-switch v-model="editMode" active-text="自由编辑" inactive-text="约束编辑" active-color="#13ce66"
|
<el-switch v-model="editMode" active-text="自由编辑" inactive-text="约束编辑" active-color="#13ce66"
|
||||||
inactive-color="#13ce66">
|
inactive-color="#13ce66">
|
||||||
</el-switch>
|
</el-switch>
|
||||||
@ -197,7 +197,7 @@ export default {
|
|||||||
},
|
},
|
||||||
copyBro() {
|
copyBro() {
|
||||||
copyBroCode(this.__rawVueInfo__);
|
copyBroCode(this.__rawVueInfo__);
|
||||||
this.$emit('codeRefresh');
|
this.$store.commit('onDragEnd');
|
||||||
},
|
},
|
||||||
onShow() {
|
onShow() {
|
||||||
// 这种方式适用于常规模式下的初始化,因为这个实例初始化后不会被销毁,一直常驻内存。但又不能受到其它实例销毁时的影响,所以需要明确的再次初始化。
|
// 这种方式适用于常规模式下的初始化,因为这个实例初始化后不会被销毁,一直常驻内存。但又不能受到其它实例销毁时的影响,所以需要明确的再次初始化。
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<el-drawer v-model="drawer" :with-header="false" size="70%" direction="btt">
|
<el-drawer v-model="drawer" :with-header="false" size="70%" direction="btt">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<div style="text-algin: center;">组件结构检视图
|
<div style="text-align: center;">组件结构检视图
|
||||||
<br>
|
<br>
|
||||||
<span style="font-size:12px;">Components
|
<span style="font-size:12px;">Components
|
||||||
Structure</span>
|
Structure</span>
|
||||||
@ -12,26 +12,13 @@
|
|||||||
<el-col :span="16" style="height: 100%;">
|
<el-col :span="16" style="height: 100%;">
|
||||||
<div style="overflow: scroll;height:100%; margin: 0 20px;padding: 10px;">
|
<div style="overflow: scroll;height:100%; margin: 0 20px;padding: 10px;">
|
||||||
|
|
||||||
<!-- <vue-nestable v-model="treeData" @change="onLevelChange">
|
<nested-draggable :data="treeData" />
|
||||||
<template v-slot="{ item }">
|
|
||||||
<vue-nestable-handle :item="item">
|
|
||||||
<el-icon class="icon-s"><rank /></el-icon>
|
|
||||||
</vue-nestable-handle>
|
|
||||||
|
|
||||||
<span @click="onNodeClick(item)">{{ item.text }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:placeholder>
|
|
||||||
<div><b>The editor is empty.</b></div>
|
|
||||||
</template>
|
|
||||||
</vue-nestable> -->
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<attribute-input ref="attributeInput" :enableRemoveButton="true" v-if="currentEditRawInfo && drawer"
|
<attribute-input ref="attributeInput" :enableRemoveButton="true" v-if="currentEditRawInfo" @save="onSaveAttr"
|
||||||
@save="onSaveAttr" shortcutInitMode="auto" @remove="onRemove" @codeRefresh="codeRefresh"
|
shortcutInitMode="auto" @remove="onRemove" :__rawVueInfo__="currentEditRawInfo">
|
||||||
:__rawVueInfo__="currentEditRawInfo">
|
|
||||||
</attribute-input>
|
</attribute-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -41,81 +28,33 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import "./halower-tree.min.css";
|
|
||||||
import "@/assets/nestable.css"
|
|
||||||
import { isObject, getRawComponentKey, getRawComponentContent } from "@/utils/common";
|
import { isObject, getRawComponentKey, getRawComponentContent } from "@/utils/common";
|
||||||
// import { VueNestable, VueNestableHandle } from 'vue-nestable';
|
import nestedDraggable from './nested.vue'
|
||||||
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['visible'],
|
props: ['visible'],
|
||||||
emits: ['codeRefresh', 'onLevelChange', 'remove', 'save', 'update:visible'],
|
emits: ['onLevelChange', 'remove', 'save', 'update:visible', 'reRender'],
|
||||||
components: {
|
components: {
|
||||||
AttributeInput: resolve => { require(["./AttributeInput"], resolve) },
|
AttributeInput: defineAsyncComponent(() => import("@/components/AttributeInput.vue")),
|
||||||
// VueNestable,
|
nestedDraggable
|
||||||
// VueNestableHandle
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 在此自动生成
|
// 在此自动生成
|
||||||
treeData: [],
|
treeData: [],
|
||||||
currentEditRawInfo: null
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
beforeCreate() { },
|
beforeCreate() { },
|
||||||
created() { },
|
created() { },
|
||||||
beforeMount() { },
|
beforeMount() { },
|
||||||
mounted() { },
|
mounted() {
|
||||||
|
},
|
||||||
beforeUpdate() { },
|
beforeUpdate() { },
|
||||||
updated() { },
|
updated() { },
|
||||||
destoryed() { },
|
destoryed() { },
|
||||||
methods: {
|
methods: {
|
||||||
// 在此自动生成
|
|
||||||
request() {
|
|
||||||
// 网络请求,可选
|
|
||||||
},
|
|
||||||
codeRefresh() {
|
|
||||||
this.$emit('codeRefresh');
|
|
||||||
},
|
|
||||||
onLevelChange(value, options) {
|
|
||||||
this.$emit('onLevelChange', value.id, options.pathTo);
|
|
||||||
},
|
|
||||||
|
|
||||||
convertStructure(rawInfo) {
|
|
||||||
const title = getRawComponentKey(rawInfo);
|
|
||||||
const object = rawInfo[title];
|
|
||||||
const children = [];
|
|
||||||
if (isObject(object)) {
|
|
||||||
for (const key in object) {
|
|
||||||
if (object.hasOwnProperty(key)) {
|
|
||||||
if (key === '__children') {
|
|
||||||
const element = object[key];
|
|
||||||
|
|
||||||
element.forEach(item => {
|
|
||||||
const temp = this.convertStructure(item);
|
|
||||||
temp && children.push(temp);
|
|
||||||
})
|
|
||||||
} else if (isObject(object[key])) {
|
|
||||||
// 组成一个新的结构,适配只有一个子节点的数据结构
|
|
||||||
const __obj = {};
|
|
||||||
__obj[key] = object[key];
|
|
||||||
const child = this.convertStructure(__obj);
|
|
||||||
child && children.push(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
text: title,
|
|
||||||
expanded: true,
|
|
||||||
children: children,
|
|
||||||
rawInfo: rawInfo,
|
|
||||||
id: getRawComponentContent(rawInfo).lc_id
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onNodeClick(nodeInfo) {
|
onNodeClick(nodeInfo) {
|
||||||
this.currentEditRawInfo = nodeInfo.rawInfo;
|
this.currentEditRawInfo = nodeInfo.rawInfo;
|
||||||
@ -131,12 +70,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateCode(codeRawInfo) {
|
updateCode(codeRawInfo) {
|
||||||
this.treeData = [this.convertStructure(codeRawInfo)];
|
this._codeRawInfo = codeRawInfo;
|
||||||
|
const content = getRawComponentContent(codeRawInfo);
|
||||||
|
const children = content.__children;
|
||||||
|
this.treeData = children;
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
canInitShortcut(newValue) {
|
renderCount(){
|
||||||
|
// 这里利用了vuedraggable v-model的特性,它会更改对象本身的引用
|
||||||
|
this.$emit('reRender', this._codeRawInfo);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -148,8 +92,21 @@ export default {
|
|||||||
this.$emit('update:visible', false);
|
this.$emit('update:visible', false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderCount(){
|
||||||
|
return this.$store.state.renderCount;
|
||||||
|
},
|
||||||
|
|
||||||
canInitShortcut() {
|
canInitShortcut() {
|
||||||
return this.currentEditRawInfo !== null && this.drawer;
|
return this.currentEditRawInfo !== null && this.drawer;
|
||||||
|
},
|
||||||
|
currentEditRawInfo() {
|
||||||
|
if (this.$store.state.currentEditComp) {
|
||||||
|
const vccData = this.$store.state.currentEditComp.vccData;
|
||||||
|
return window.tree[vccData.lc_id];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fillter: {},
|
fillter: {},
|
||||||
|
576
src/components/halower-tree.min.css
vendored
576
src/components/halower-tree.min.css
vendored
File diff suppressed because one or more lines are too long
89
src/components/nested.vue
Normal file
89
src/components/nested.vue
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<draggable class="dragArea" tag="ul" :list="data" @start="onStartDrag" @choose="onClick"
|
||||||
|
@end="onEndDrag">
|
||||||
|
<template #item="{ element }">
|
||||||
|
<li class="itemArea">
|
||||||
|
<p>{{ getRawComponentKey(element) }}</p>
|
||||||
|
<nested-draggable :data="getRawComponentContent(element).__children" />
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import draggable from "vuedraggable";
|
||||||
|
import { getRawComponentKey, getRawComponentContent } from "@/utils/common";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
data: {
|
||||||
|
required: true,
|
||||||
|
type: Array
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getRawComponentKey,
|
||||||
|
getRawComponentContent,
|
||||||
|
onStartDrag(event) {
|
||||||
|
event.item.classList.add("is-dragging");
|
||||||
|
},
|
||||||
|
onClick(event) {
|
||||||
|
if(this.$store.state.currentEditComp){
|
||||||
|
this.$store.state.currentEditComp.item.classList.remove("is-dragging");
|
||||||
|
}
|
||||||
|
|
||||||
|
event.item.classList.add("is-dragging");
|
||||||
|
|
||||||
|
event.vccData = getRawComponentContent(this.data[event.oldIndex]);
|
||||||
|
this.$store.commit('storeCurrentEditComp', event);
|
||||||
|
},
|
||||||
|
onEndDrag(event) {
|
||||||
|
event.item.classList.remove("is-dragging");
|
||||||
|
|
||||||
|
this.$store.commit('onDragEnd');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
draggable
|
||||||
|
},
|
||||||
|
name: "nested-draggable"
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.dragArea {
|
||||||
|
min-height: 20px;
|
||||||
|
border: 1px dashed rgb(126, 126, 128);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding-inline-start: 30px;
|
||||||
|
padding-right: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemArea {
|
||||||
|
min-height: 20px;
|
||||||
|
border: 1px dashed rgb(126, 126, 128);
|
||||||
|
padding-right: 2px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding-inline-start: 10px;
|
||||||
|
margin: 10px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-dragging {
|
||||||
|
background-color: rgba(106, 127, 233, 0.274);
|
||||||
|
border: 1px dashed rgb(73, 100, 241);
|
||||||
|
-webkit-border-radius: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
</style>
|
24
src/libs/store.js
Normal file
24
src/libs/store.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { createStore } from 'vuex'
|
||||||
|
|
||||||
|
const store = createStore({
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
count: 0,
|
||||||
|
currentEditComp: null,
|
||||||
|
renderCount: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
increment(state) {
|
||||||
|
state.count++
|
||||||
|
},
|
||||||
|
storeCurrentEditComp(state, newComp) {
|
||||||
|
state.currentEditComp = newComp;
|
||||||
|
},
|
||||||
|
onDragEnd(state) {
|
||||||
|
state.renderCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
globalApp.use(store);
|
10
src/main.js
10
src/main.js
@ -1,4 +1,4 @@
|
|||||||
import { createApp, compile } from "vue";
|
import { createApp } from "vue";
|
||||||
import ElementPlus from "element-plus";
|
import ElementPlus from "element-plus";
|
||||||
import { QuestionFilled, CirclePlus, DocumentCopy, Delete, Refresh, Minus } from "@element-plus/icons";
|
import { QuestionFilled, CirclePlus, DocumentCopy, Delete, Refresh, Minus } from "@element-plus/icons";
|
||||||
|
|
||||||
@ -19,8 +19,12 @@ function createBaseApp(renderComponent = {}) {
|
|||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
createBaseApp(APP).mount("#app");
|
const globalApp = createBaseApp(APP)
|
||||||
|
globalApp.mount("#app");
|
||||||
|
|
||||||
// 内部需要同样配置的全局Vue
|
// 内部需要同样配置的全局Vue
|
||||||
self.createBaseApp = createBaseApp;
|
self.createBaseApp = createBaseApp;
|
||||||
self.compile = compile;
|
self.globalApp = globalApp; // 内部需要使用Vuex
|
||||||
|
|
||||||
|
|
||||||
|
import("@/libs/store.js");
|
Loading…
x
Reference in New Issue
Block a user