This commit is contained in:
wangchen147 2023-11-27 15:33:54 +08:00
parent a0c9a04155
commit 85422756a9
18 changed files with 654 additions and 460 deletions

View File

@ -45,6 +45,6 @@ export function getModuleVersion() {
* @param params
* @returns
*/
export function downloadVersion(addon) {
return request.post(`addon/download/${addon}`, {}, {showSuccessMessage: true})
export function downloadVersion(params: Record<string, any>) {
return request.post(`addon/download/${params.addon}`, params, { showSuccessMessage: true })
}

View File

@ -16,6 +16,7 @@
"refreshPage": "刷新",
"placeholderTemplate": "请选择一个模板",
"placeholderMyPage": "请选择一个微页面",
"placeholderOtherPage": "请选择一个页面",
"developTitle": "开发环境配置",
"wapDomain": "wap域名WAP_DOMAIN",
"wapDomainPlaceholder": "请输入wap域名"

View File

@ -0,0 +1,12 @@
{
"preview": "预览",
"weapp": "微信小程序",
"wechat": "微信公众号",
"link": "链接",
"copy": "复制",
"copySuccess": "复制成功",
"weappNotSet": "小程序未配置",
"developTitle": "开发环境配置",
"wapDomain": "wap域名WAP_DOMAIN",
"wapDomainPlaceholder": "请输入wap域名"
}

View File

@ -540,7 +540,7 @@
save((id: number) => {
id = diyStore.id || id;
let url = router.resolve({
path: '/preview/wap',
path: '/site/preview/wap',
query: {
page: page.value + '?id=' + id
}

View File

@ -1,431 +1,453 @@
<template>
<div class="flex flex-wrap">
<div class="page-item relative bg-no-repeat ml-[20px] mr-[40px] mt-[20px] bg-[#f7f7f7] w-[300px] pt-[80px] pb-[20px]"
:class="{ 'cursor-pointer': !item.isDisabledPop }" v-for="(item, key) in page" :key="key">
<p class="absolute top-[46px] left-[50%] translate-x-[-50%] text-[14px] truncate w-[130px] text-center">
{{ item.use_template.title }}</p>
<div class="flex flex-wrap">
<div class="page-item relative bg-no-repeat ml-[20px] mr-[40px] mt-[20px] bg-[#f7f7f7] w-[300px] pt-[80px] pb-[20px]"
:class="{ 'cursor-pointer' : !item.isDisabledPop }" v-for="(item,key) in page" :key="key">
<p class="absolute top-[46px] left-[50%] translate-x-[-50%] text-[14px] truncate w-[130px] text-center">
{{item.use_template.title}}</p>
<div v-show="item.use_template.url" class="w-[282px] h-[493px] mx-auto">
<iframe :id="'previewIframe_' + key" v-show="item.loadingIframe" class="w-[282px] h-[493px] mx-auto"
:src="item.use_template.wapPreview" frameborder="0"></iframe>
<div v-show="item.loadingDev" class="w-[282px] h-[493px] mx-auto bg-body pt-[20px] px-[20px]">
<div class="font-bold text-xl mb-[40px]">{{ t('developTitle') }}</div>
<div class="mb-[20px] flex flex-col">
<text class="mb-[10px]">{{ t('wapDomain') }}</text>
<el-input v-model="wapDomain" :placeholder="t('wapDomainPlaceholder')" clearable />
</div>
<el-button type="primary" @click="saveDomain()">{{ t('confirm') }}</el-button>
</div>
</div>
<div v-show="item.use_template.url" class="w-[282px] h-[493px] mx-auto">
<iframe :id="'previewIframe_' + key" v-show="item.loadingIframe" class="w-[282px] h-[493px] mx-auto"
:src="item.use_template.wapPreview" frameborder="0"></iframe>
<div v-show="item.loadingDev" class="w-[282px] h-[493px] mx-auto bg-body pt-[20px] px-[20px]">
<div class="font-bold text-xl mb-[40px]">{{t('developTitle')}}</div>
<div class="mb-[20px] flex flex-col">
<text class="mb-[10px]">{{ t('wapDomain') }}</text>
<el-input v-model="wapDomain" :placeholder="t('wapDomainPlaceholder')" clearable/>
</div>
<el-button type="primary" @click="saveDomain()">{{ t('confirm') }}</el-button>
</div>
</div>
<div v-show="!item.use_template.wapPreview" class="overflow-hidden w-[282px] h-[493px] mx-auto">
<img class="max-w-full" v-if="item.use_template.cover" :src="img(item.use_template.cover)" />
</div>
<div v-show="!item.use_template.wapPreview" class="overflow-hidden w-[282px] h-[493px] mx-auto">
<img class="max-w-full" v-if="item.use_template.cover" :src="img(item.use_template.cover)"/>
</div>
<p class="text-[12px] text-[#999] mt-[10px] mx-auto truncate text-center w-[250px]">
{{ item.use_template.desc }}</p>
<p class="text-[12px] text-[#999] mt-[10px] mx-auto truncate text-center w-[250px]">
{{item.use_template.desc}}</p>
<div class="item-hide absolute inset-x-0 inset-y-0 bg-black bg-opacity-50 text-center"
:class="{ 'disabled': item.isDisabledPop }">
<div
class="item-btn-box absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-col flex-wrap">
<el-button @click="show(key, item)">{{ t('changePage') }}</el-button>
<el-button @click="toDecorate(item.use_template)"
v-show="item.use_template.mode != 'other' || item.use_template.action == 'decorate'">{{
t('decorate') }}
</el-button>
<el-button @click="toPreview(item.use_template)">{{ t('preview') }}</el-button>
</div>
</div>
<div class="item-hide absolute inset-x-0 inset-y-0 bg-black bg-opacity-50 text-center"
:class="{ 'disabled' : item.isDisabledPop }">
<div class="item-btn-box absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] flex flex-col flex-wrap">
<el-button @click="show(key,item)">{{ t('changePage') }}</el-button>
<el-button @click="toDecorate(item.use_template)"
v-show="item.use_template.mode != 'other' || item.use_template.action == 'decorate'">{{
t('decorate') }}
</el-button>
<el-button @click="toPreview(item.use_template)">{{ t('preview') }}</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
<el-dialog v-model="showDialog" :title="t('changeTemplate')" width="400px" :close-on-press-escape="false"
:destroy-on-close="true" :close-on-click-modal="false">
<el-dialog v-model="showDialog" :title="t('changeTemplate')" width="400px" :close-on-press-escape="false"
:destroy-on-close="true" :close-on-click-modal="false">
<el-form :model="form" label-width="0px" v-if="formData.type">
<el-form-item label="">
<div>{{ t('hopeBeforeTip') }}<span class="text-primary px-[5px]">{{ page[formData.type].title
}}</span>{{ t('hopeAfterTip') }}
</div>
</el-form-item>
<el-form :model="form" label-width="0px" v-if="formData.type">
<el-form-item label="">
<div>{{t('hopeBeforeTip')}}<span class="text-primary px-[5px]">{{ page[formData.type].title }}</span>{{t('hopeAfterTip')}}
</div>
</el-form-item>
<el-form-item label="">
<el-select v-model="hope" class="w-full">
<el-option :label="t('changeTemplateTip') + ' ' + page[formData.type].title + ' ' + t('template')"
value="template" />
<el-option :label="t('changeMyPageTip') + ' ' + page[formData.type].title" value="diy" />
<el-option :label="t('changeOtherPageTip') + ' ' + page[formData.type].title" value="other" />
</el-select>
</el-form-item>
<el-form-item label="">
<el-select v-model="hope" class="w-full">
<el-option :label="t('changeTemplateTip') + ' ' + page[formData.type].title + ' ' + t('template')"
value="template"/>
<el-option :label="t('changeMyPageTip') + ' ' + page[formData.type].title" value="diy"/>
<el-option :label="t('changeOtherPageTip') + ' ' + page[formData.type].title" value="other"/>
</el-select>
</el-form-item>
<el-form-item label="" v-show="hope == 'template'">
<el-select v-model="formData.template" class="w-full">
<el-option v-for="(item, key) in page[formData.type].template" :label="item.title" :value="key" />
</el-select>
</el-form-item>
<el-form-item label="" v-show="hope == 'template'">
<el-select v-model="formData.template" class="w-full">
<el-option v-for="(item, key) in page[formData.type].template" :label="item.title" :value="key"/>
</el-select>
</el-form-item>
<el-form-item label="" v-show="hope == 'diy'">
<el-select v-model="formData.id" class="w-full">
<el-option v-for="(item, index) in page[formData.type].my_page" :label="item.title" :value="item.id" />
</el-select>
<div class="mt-[10px]">
<span class="cursor-pointer text-primary mr-[10px]" @click="toDiyList">{{ t('createPage') }}</span>
<span class="cursor-pointer text-primary" @click="refreshMyPage">{{ t('refreshPage') }}</span>
</div>
</el-form-item>
<el-form-item label="" v-show="hope == 'diy'">
<el-select v-model="formData.id" class="w-full">
<el-option v-for="(item, index) in page[formData.type].my_page" :label="item.title"
:value="item.id"/>
</el-select>
<div class="mt-[10px]">
<span class="cursor-pointer text-primary mr-[10px]" @click="toDiyList">{{ t('createPage') }}</span>
<span class="cursor-pointer text-primary" @click="refreshMyPage">{{ t('refreshPage') }}</span>
</div>
</el-form-item>
<el-form-item label="" v-show="hope == 'other'">
<el-select v-model="formData.page" class="w-full">
<el-option v-for="(item, index) in page[formData.type].other_page" :label="item.title"
:value="item.page" />
</el-select>
</el-form-item>
<el-form-item label="" v-show="hope == 'other'">
<el-select v-model="formData.page" class="w-full">
<el-option v-for="(item, index) in page[formData.type].other_page" :label="item.title"
:value="item.page"/>
</el-select>
</el-form-item>
</el-form>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel')}}</el-button>
<el-button type="primary" @click="save">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="save">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import { t } from '@/lang'
import { img } from '@/utils/common'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { getDecoratePage, getDiyList, changeTemplate } from '@/app/api/diy'
import storage from '@/utils/storage'
import {reactive, ref, watch} from 'vue'
import {t} from '@/lang'
import {img} from '@/utils/common'
import {useRouter} from 'vue-router'
import {ElMessage} from 'element-plus'
import {getDecoratePage, getDiyList, changeTemplate} from '@/app/api/diy'
import storage from '@/utils/storage'
const page: any = reactive({})
const showDialog = ref(false)
const router = useRouter()
const hope = ref('template')
const wapDomain = ref('')
const page: any = reactive({})
const showDialog = ref(false)
const router = useRouter()
const hope = ref('template')
const wapDomain = ref('')
//
const formData = reactive({
type: '',
mode: '',
template: '',
id: '',
page: '',
title: '',
action: ''
})
//
const formData = reactive({
type: '',
name: '',
mode: '',
template: '',
id: '',
page: '',
title: '',
action: ''
})
//
const refreshData = () => {
formData.type = ''
formData.mode = ''
formData.template = ''
formData.id = ''
formData.page = ''
formData.title = ''
formData.action = ''
getDecoratePage({}).then(res => {
for (const key in res.data) {
page[key] = res.data[key]
}
//
const refreshData = () => {
formData.type = '';
formData.name = '';
formData.mode = '';
formData.template = '';
formData.id = '';
formData.page = '';
formData.title = '';
formData.action = '';
getDecoratePage({}).then((res => {
for (let key in res.data) {
page[key] = res.data[key]
}
for (const key in page) {
if (page[key].use_template.url) {
page[key].loadingIframe = false // iframe
page[key].loadingDev = false //
page[key].isDisabledPop = false //
page[key].difference = 0 // 1000wap
for (let key in page) {
if (page[key].use_template.url) {
wapDomain.value = page[key].domain_url.wap_domain
page[key].wapUrl = page[key].domain_url.wap_url
page[key].loadingIframe = false; // iframe
page[key].loadingDev = false; //
page[key].isDisabledPop = false; //
page[key].difference = 0; // 1000wap
if (import.meta.env.MODE == 'development') {
// wap
if (wapDomain.value) {
page[key].wapUrl = wapDomain.value + '/wap'
setDomain(key)
}
if (storage.get('wap_domain')) {
page[key].wapUrl = storage.get('wap_domain')
setDomain(key)
wapDomain.value = page[key].domain_url.wap_domain;
page[key].wapUrl = page[key].domain_url.wap_url;
if (import.meta.env.MODE == 'development') {
// wap
if (wapDomain.value) {
page[key].wapUrl = wapDomain.value + '/wap';
setDomain(key);
}
if (storage.get('wap_domain')) {
page[key].wapUrl = storage.get('wap_domain')
setDomain(key);
}
}
setDomain(key);
}
setDomain(key)
}
}
})
}
refreshData()
// uni-app
window.addEventListener('message', (event) => {
try {
const data = JSON.parse(event.data)
if (['appOnLaunch', 'appOnReady'].indexOf(data.type) != -1) {
for (const key in page) {
page[key].loadingDev = false //
page[key].loadingIframe = true // iframe
const loadTime = new Date().getTime()
page[key].difference = loadTime - page[key].timeIframe
page[key].isDisabledPop = false //
}
}
} catch (e) {
for (const key in page) {
initLoad(key)
}
console.log('后台接受数据错误', e)
}));
}
}, false)
// uniapp
const postMessage = (key: string) => {
const diyData = JSON.stringify({
type: 'appOnReady',
message: '加载完成'
})
if (window['previewIframe_' + key]) window['previewIframe_' + key].contentWindow.postMessage(diyData, '*')
}
refreshData();
//
const initLoad = (key: string) => {
page[key].loadingDev = true
page[key].isDisabledPop = true
page[key].loadingIframe = false
}
// uni-app
window.addEventListener('message', (event) => {
try {
let data = JSON.parse(event.data);
if (['appOnLaunch', 'appOnReady'].indexOf(data.type) != -1) {
for (let key in page) {
page[key].loadingDev = false; //
page[key].loadingIframe = true; // iframe
var loadTime = new Date().getTime();
page[key].difference = loadTime - page[key].timeIframe;
page[key].isDisabledPop = false; //
}
}
} catch (e) {
for (let key in page) {
initLoad(key);
}
console.log('后台接受数据错误', e)
}
}, false);
const saveDomain = () => {
if (wapDomain.value.trim().length == 0) {
ElMessage({
type: 'warning',
message: `${t('wapDomainPlaceholder')}`
// uniapp
const postMessage = (key: string) => {
var diyData = JSON.stringify({
type: 'appOnReady',
message: '加载完成'
});
if (window['previewIframe_' + key]) window['previewIframe_' + key].contentWindow.postMessage(diyData, '*');
};
//
const initLoad = (key: string) => {
page[key].loadingDev = true;
page[key].isDisabledPop = true;
page[key].loadingIframe = false;
}
const saveDomain = () => {
if (wapDomain.value.trim().length == 0) {
ElMessage({
type: 'warning',
message: `${t('wapDomainPlaceholder')}`,
});
return;
}
let wapUrl = wapDomain.value + '/wap';
storage.set({key: 'wap_domain', data: wapUrl});
for (let key in page) {
if (page[key].use_template.url) {
page[key].wapUrl = wapUrl;
setDomain(key);
}
}
setTimeout(() => {
for (let key in page) {
if (page[key].use_template.url) {
page[key].loadingIframe = true; // iframe
page[key].loadingDev = false; //
page[key].isDisabledPop = false; //
}
}
}, 100 * 3);
}
const setDomain = (key: string) => {
page[key].use_template.wapPreview = page[key].wapUrl + page[key].use_template.url;
page[key].timeIframe = new Date().getTime();
postMessage(key);
setTimeout(() => {
if (page[key].difference == 0) initLoad(key);
}, 1000 * 2);
}
const show = (key: string, data: any) => {
//
showDialog.value = true;
hope.value = data.use_template.hope;
formData.type = key;
formData.name = data.use_template.name;
formData.mode = data.use_template.mode;
formData.action = data.use_template.action;
if (hope.value == 'template') {
formData.template = data.use_template.template;
} else if (hope.value == 'diy') {
formData.id = data.use_template.id;
} else if (hope.value == 'other') {
formData.page = data.use_template.page;
formData.title = data.use_template.title;
}
}
//
const toDecorate = (data: any) => {
let query: any = {
back: '/site/diy/index'
};
if (data.id) {
query.id = data.id;
} else if (data.name) {
query.name = data.name;
} else if (data.url) {
query.url = data.url;
}
let url = router.resolve({
path: '/decorate/edit',
query
});
window.open(url.href);
}
//
const toPreview = (data: any) => {
let page = data.page;
if (data.url) {
page = data.url;
} else if (data.id) {
page += '?id=' + data.id;
}
let url = router.resolve({
path: '/preview/wap',
query: {
page
}
});
window.open(url.href);
}
//
const toDiyList = (data: any) => {
let url = router.resolve({
path: '/diy/list'
});
window.open(url.href);
}
//
const refreshMyPage = () => {
getDiyList({type: formData.type}).then((res) => {
let isExist = true; //
for (let i = 0; i < res.data.length; i++) {
if (formData.id == res.data[i].id) {
isExist = false;
break;
}
}
if (isExist) {
formData.id = '';
}
page[formData.type].my_page = {};
Object.assign(page[formData.type].my_page, res.data);
})
return
}
const wapUrl = wapDomain.value + '/wap'
storage.set({ key: 'wap_domain', data: wapUrl })
for (const key in page) {
if (page[key].use_template.url) {
page[key].wapUrl = wapUrl
setDomain(key)
}
}
setTimeout(() => {
for (const key in page) {
if (page[key].use_template.url) {
page[key].loadingIframe = true // iframe
page[key].loadingDev = false //
page[key].isDisabledPop = false //
watch(
() => hope.value,
(newValue, oldValue) => {
//
if (newValue == 'template') {
//
formData.id = '';
formData.page = '';
formData.action = 'decorate';
formData.name = formData.type;
} else if (newValue == 'diy') {
//
formData.mode = 'diy';
formData.template = '';
formData.page = '';
formData.action = 'decorate';
formData.name = formData.type;
} else if (newValue == 'other') {
//
formData.mode = 'other';
formData.template = '';
formData.id = '';
}
}
}, 100 * 3)
}
)
const setDomain = (key: string) => {
page[key].use_template.wapPreview = page[key].wapUrl + page[key].use_template.url
page[key].timeIframe = new Date().getTime()
postMessage(key)
setTimeout(() => {
if (page[key].difference == 0) initLoad(key)
}, 1000 * 2)
}
const show = (key: string, data: any) => {
//
showDialog.value = true
hope.value = data.use_template.hope
formData.type = key
formData.mode = data.use_template.mode
formData.action = data.use_template.action
if (hope.value == 'template') {
formData.template = data.use_template.template
} else if (hope.value == 'diy') {
formData.id = data.use_template.id
} else if (hope.value == 'other') {
formData.page = data.use_template.page
formData.title = data.use_template.title
}
}
//
const toDecorate = (data: any) => {
const query: any = {
back: '/site/diy/index'
}
if (data.id) {
query.id = data.id
} else if (data.name) {
query.name = data.name
} else if (data.url) {
query.url = data.url
}
const url = router.resolve({
path: '/decorate/edit',
query
})
window.open(url.href)
}
//
const toPreview = (data: any) => {
let page = data.page
if (data.url) {
page = data.url
} else if (data.id) {
page += '?id=' + data.id
}
const url = router.resolve({
path: '/preview/wap',
query: {
page
}
})
window.open(url.href)
}
//
const toDiyList = (data: any) => {
const url = router.resolve({
path: '/site/diy/list'
})
window.open(url.href)
}
//
const refreshMyPage = () => {
getDiyList({ type: formData.type }).then((res) => {
let isExist = true //
for (let i = 0; i < res.data.length; i++) {
if (formData.id == res.data[i].id) {
isExist = false
break
//
watch(
() => formData.template,
(newValue, oldValue) => {
if (newValue) {
formData.mode = page[formData.type].template[newValue].mode;
}
}
if (isExist) {
formData.id = ''
}
page[formData.type].my_page = {}
Object.assign(page[formData.type].my_page, res.data)
})
}
)
watch(
() => hope.value,
(newValue, oldValue) => {
//
if (newValue == 'template') {
formData.id = ''
formData.page = ''
formData.action = 'decorate'
} else if (newValue == 'diy') {
formData.mode = 'diy'
formData.template = ''
formData.page = ''
formData.action = 'decorate'
} else if (newValue == 'other') {
formData.mode = 'other'
formData.template = ''
formData.id = ''
}
}
)
watch(
() => formData.template,
(newValue, oldValue) => {
if (newValue) {
formData.mode = page[formData.type].template[newValue].mode
}
}
)
watch(
() => formData.page,
(newValue, oldValue) => {
if (newValue) {
for (let i = 0; i < page[formData.type].other_page.length; i++) {
if (page[formData.type].other_page[i].page == newValue) {
formData.title = page[formData.type].other_page[i].title
formData.action = page[formData.type].other_page[i].action
break
//
watch(
() => formData.page,
(newValue, oldValue) => {
if (newValue) {
for (let i = 0; i < page[formData.type].other_page.length; i++) {
if (page[formData.type].other_page[i].page == newValue) {
formData.name = page[formData.type].other_page[i].name;
formData.title = page[formData.type].other_page[i].title;
formData.action = page[formData.type].other_page[i].action;
break;
}
}
}
}
}
)
)
const isRepeat = ref(false)
const save = () => {
if (hope.value == 'template') {
if (formData.template == '') {
ElMessage({
type: 'warning',
message: `${t('placeholderTemplate')}`
})
return
}
} else if (hope.value == 'diy') {
if (formData.id == '') {
ElMessage({
type: 'warning',
message: `${t('placeholderMyPage')}`
})
return
const isRepeat = ref(false)
const save = () => {
if (hope.value == 'template') {
if (formData.template == '') {
ElMessage({
type: 'warning',
message: `${t('placeholderTemplate')}`,
});
return;
}
} else if (hope.value == 'diy') {
if (formData.id == '') {
ElMessage({
type: 'warning',
message: `${t('placeholderMyPage')}`,
});
return;
}
} else if (hope.value == 'other') {
if (formData.page == '') {
ElMessage({
type: 'warning',
message: `${t('placeholderOtherPage')}`,
});
return;
}
}
if (isRepeat.value) return
isRepeat.value = true
changeTemplate({
...formData
}).then((res) => {
isRepeat.value = false;
showDialog.value = false;
refreshData();
})
}
if (isRepeat.value) return
isRepeat.value = true
changeTemplate({
...formData
}).then((res) => {
isRepeat.value = false
showDialog.value = false
refreshData()
})
}
</script>
<style lang="scss" scoped>
.page-item {
.page-item {
background-image: url(@/app/assets/images/iphone_bg.png);
background-color: var(--el-bg-color);
background-size: 100%;
background-image: url(@/app/assets/images/iphone_bg.png);
background-color: var(--el-bg-color);
background-size: 100%;
.item-hide {
display: none;
.item-hide {
display: none;
.item-btn-box {
.item-btn-box {
button {
height: 35px;
width: 100px;
button {
height: 35px;
width: 100px;
&~button {
margin-top: 15px;
margin-left: 0;
}
}
& ~ button {
margin-top: 15px;
margin-left: 0;
}
}
}
}
}
}
&:hover {
.item-hide:not(.disabled) {
display: block !important;
}
}
}
&:hover {
.item-hide:not(.disabled) {
display: block !important;
}
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="main-container w-[375px] mx-auto mt-[20px] mb-[40px] relative">
<div class="flex h-full">
<div class="flex full-container">
<iframe v-show="loadingIframe" class="w-[375px]" :src="wapPreview" frameborder="0"
id="previewIframe"></iframe>
<div v-show="loadingDev" class="w-[375px] border border-slate-100 bg-body pt-[20px] px-[20px]">
@ -121,6 +121,7 @@
const setDomain = () => {
if (route.query.page) {
wapPreview.value = `${wapUrl.value}${route.query.page}`;
console.log(wapPreview.value)
// errorCorrectionLevelLH()
QRCode.toDataURL(wapPreview.value, {errorCorrectionLevel: 'L', margin: 0, width: 100}).then(url => {
wapImage.value = url

View File

@ -294,7 +294,7 @@
</div>
</div>
<div v-show="installStep == 2" class="h-[50vh] mt-[20px]">
<terminal name="my-terminal" :context="currAddon" :init-log="null" :show-header="false"
<terminal :name="currAddon" :context="currAddon" :init-log="null" :show-header="false"
:show-log-time="true" />
</div>
<div v-show="installStep == 3" class="h-[50vh] mt-[20px] flex flex-col">
@ -635,8 +635,8 @@ const handleCloudInstall = () => {
cloudInstallAddon({ addon: currAddon.value }).then(res => {
installStep.value = 2
terminalApi.execute('my-terminal', 'clear')
terminalApi.pushMessage('my-terminal', { content: '开始安装插件', class: 'info' })
terminalApi.execute(currAddon.value, 'clear')
terminalApi.pushMessage(currAddon.value, { content: '开始安装插件', class: 'info' })
getInstallTask()
cloudInstalling.value = false
}).catch((res) => {
@ -670,11 +670,11 @@ const getCloudInstallLog = () => {
if (data[0] && data[0].length && installShowDialog.value == true) {
data[0].forEach(item => {
if (!installLog.includes(item.action)) {
terminalApi.pushMessage('my-terminal', { content: `正在执行:${item.action}` })
terminalApi.pushMessage(currAddon.value, { content: `正在执行:${item.action}` })
installLog.push(item.action)
if (item.code == 0) {
terminalApi.pushMessage('my-terminal', { content: item.msg, class: 'error' })
terminalApi.pushMessage(currAddon.value, { content: item.msg, class: 'error' })
}
}
})

View File

@ -7,8 +7,18 @@
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="toSiteLink">切换站点</el-dropdown-item>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
<el-dropdown-item @click="toSiteLink">
<div class="flex items-center leading-[1] py-[5px]">
<span class="iconfont iconqiehuan ml-[4px] !text-[14px] mr-[10px]"></span>
<span class="text-[14px]">切换站点</span>
</div>
</el-dropdown-item>
<el-dropdown-item command="logout">
<div class="flex items-center leading-[1] py-[2px]">
<span class="iconfont icontuichudenglu !text-[21px] mr-[8px]"></span>
<span class="text-[14px]">退出登录</span>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -35,4 +45,8 @@ const toSiteLink = ()=>{
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.el-popper .el-dropdown-menu{
width: 150px;
}
</style>

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 3883393 */
src: url('//at.alicdn.com/t/c/font_3883393_ppfj41ck5w.woff2?t=1699092341669') format('woff2'),
url('//at.alicdn.com/t/c/font_3883393_ppfj41ck5w.woff?t=1699092341669') format('woff'),
url('//at.alicdn.com/t/c/font_3883393_ppfj41ck5w.ttf?t=1699092341669') format('truetype');
src: url('//at.alicdn.com/t/c/font_3883393_46xoryy5l7v.woff2?t=1701067532618') format('woff2'),
url('//at.alicdn.com/t/c/font_3883393_46xoryy5l7v.woff?t=1701067532618') format('woff'),
url('//at.alicdn.com/t/c/font_3883393_46xoryy5l7v.ttf?t=1701067532618') format('truetype');
}
.iconfont {
@ -13,6 +13,26 @@
-moz-osx-font-smoothing: grayscale;
}
.icontuichudenglu:before {
content: "\e64a";
}
.iconhuojian:before {
content: "\e6ff";
}
.iconicon_huojian1:before {
content: "\e6fe";
}
.iconrili1:before {
content: "\e62e";
}
.iconrili:before {
content: "\e664";
}
.iconshouquanliebiao:before {
content: "\e6fd";
}

View File

@ -144,7 +144,10 @@ class Addon extends BaseAdminController
* @return Response
*/
public function download($addon){
(new AddonService())->download($addon);
$data = $this->request->params([
['version', '']
]);
(new AddonService())->download($addon, $data['version']);
return success('DOWNLOAD_SUCCESS');
}

View File

@ -201,12 +201,16 @@ class Diy extends BaseAdminController
public function changeTemplate()
{
$data = $this->request->params([
["id", ""],
['type', ''], // 页面类型
['mode', ''], // 页面展示模式diy自定义fixed固定
['template', ''] // 模板名称
[ "id", "" ],
[ 'type', '' ], // 页面类型
[ 'name', '' ], // 页面名称标识
[ 'mode', '' ], // 页面展示模式diy自定义fixed固定other其他页面
[ 'template', '' ], // 模板名称
[ 'page', '' ], // 页面路由
[ 'title', '' ], // 页面标题
[ 'action', '' ] // 是否存在操作decorate 表示支持装修
]);
return success((new DiyService())->changeTemplate($data));
return success(( new DiyService() )->changeTemplate($data));
}
/**
@ -221,5 +225,4 @@ class Diy extends BaseAdminController
$res = (new DiyService())->getPreviewData($data);
return success($res);
}
}

View File

@ -166,8 +166,8 @@ class AddonService extends BaseAdminService
* @param string $app_key
* @return true
*/
public function download(string $app_key){
return (new CoreAddonDownloadService())->download($app_key);
public function download(string $app_key, string $version){
return (new CoreAddonDownloadService())->download($app_key, $version);
}
/**

View File

@ -43,4 +43,24 @@ class DiyConfigService extends BaseAdminService
return (new CoreDiyConfigService())->setBottomConfig($this->site_id, $data);
}
/**
* 设置启动页
* @param $data
* @return SysConfig|bool|Model
*/
public function setStartUpPageConfig($data)
{
return ( new CoreDiyConfigService() )->setStartUpPageConfig($this->site_id, $data);
}
/**
* 获取启动页配置
* @param $name
* @return array
*/
public function getStartUpPageConfig($name)
{
return ( new CoreDiyConfigService() )->getStartUpPageConfig($this->site_id, $name);
}
}

View File

@ -62,10 +62,10 @@ class DiyService extends BaseAdminService
* @throws DbException
* @throws ModelNotFoundException
*/
public function getList(array $where = [], $field = 'id,site_id,title,name,template,type,mode,is_default,share,visit_count,create_time,update_time')
public function getList(array $where = [], $field = 'id,title,name,template,type,mode,is_default,share,visit_count,create_time,update_time')
{
$order = "update_time desc";
return $this->model->where([ [ [ 'site_id', '=', $this->site_id ] ] ])->withSearch([ "title", "type", 'mode' ], $where)->field($field)->select()->order($order)->toArray();
return $this->model->where([ [ 'site_id', '=', $this->site_id ] ])->withSearch([ "title", "type", 'mode' ], $where)->field($field)->select()->order($order)->toArray();
}
/**
@ -400,14 +400,30 @@ class DiyService extends BaseAdminService
{
// 查询可装修的页面
$template = $this->getTemplate([ 'action' => 'decorate' ]);
$template = $this->getTemplate([ 'action' => 'decorate', 'type' => [ 'DIY_INDEX', 'DIY_MEMBER_INDEX' ] ]);
$specific_page = array_column($template, 'page');
// 查询其他页面,排除特定页面
$other_page = ( new DiyRouteService() )->getList();
foreach ($other_page as $ck => $cv) {
if (in_array(substr($cv[ 'page' ], 1), $specific_page) || $cv[ 'is_share' ] == 0) {
unset($other_page[ $ck ]);
}
}
$other_page = array_values($other_page);
$diy_config_service = new DiyConfigService();
// 遍历查询页面数据,使用了那套模板
foreach ($template as $k => $v) {
$template[ $k ][ 'domain_url' ] = ( new SystemService() )->getUrl();
// 查询我的微页面
$template[ $k ][ 'my_page' ] = $this->getList([ 'type' => $k ], 'id,title,name,template,type,is_default,mode');
$template[ $k ][ 'domain_url' ] = ( new SystemService() )->getUrl();
// 查询其他页面,排除特定页面
$template[ $k ][ 'other_page' ] = $other_page;
// 查询默认页面数据
$default_page_data = $this->getFirstPageData($k);
@ -421,55 +437,92 @@ class DiyService extends BaseAdminService
'desc' => $default_page_data[ 'desc' ], // 模板描述
'mode' => $default_page_data[ 'mode' ], // 页面模式diy自定义fixed固定
'hope' => 'template', // 默认选中 模板
'url' => '' // 自定义页面链接,实时预览效果
'url' => '', // 自定义页面链接,实时预览效果
'page' => $v[ 'page' ], // 页面地址
'action' => '' // 是否存在操作decorate 表示支持装修
];
// 查询页面数据
$info = $this->getInfoByName($k);
if (!empty($info)) {
$use_template[ 'id' ] = $info[ 'id' ];
$use_template[ 'title' ] = $info[ 'title' ];
$use_template[ 'template' ] = $info[ 'template' ];
$use_template[ 'mode' ] = $info[ 'mode' ];
$use_template[ 'hope' ] = $info[ 'mode' ] == 'fixed' ? 'template' : $info[ 'mode' ];
$use_template[ 'preview' ] = ''; // 默认图
$use_template[ 'desc' ] = '通过自定义装修的页面';
// 查询模板页面数
$page_data = $this->getPageData($k, $use_template[ 'template' ]);
if (!empty($page_data)) {
if ($info[ 'is_change' ] == 1) {
// 修改过模板,预览实际内容
$use_template[ 'url' ] = $v[ 'page' ] . '?id=' . $info[ 'id' ];
} else {
$use_template[ 'cover' ] = $page_data[ 'cover' ]; // 默认图
$use_template[ 'desc' ] = $page_data[ 'desc' ];
// 查询启动页配置
$start_up_page = $diy_config_service->getStartUpPageConfig($k);
if (!empty($start_up_page)) {
$use_template[ 'name' ] = $k;
if ($start_up_page[ 'mode' ] == 'other') {
if (empty($start_up_page[ 'name' ])) {
// 查询页面的名称标识
foreach ($other_page as $ck => $cv) {
if ($cv[ 'page' ] == $start_up_page[ 'page' ]) {
$use_template[ 'name' ] = $cv['name'];
break;
}
}
}
} else {
// 自定义页面,实时预览效果
$use_template[ 'url' ] = '/app/pages/index/diy?id=' . $info[ 'id' ];
// 清空模板信息
}
$use_template[ 'hope' ] = $start_up_page[ 'mode' ];
$use_template[ 'mode' ] = $start_up_page[ 'mode' ];
$use_template[ 'page' ] = $start_up_page[ 'page' ];
$use_template[ 'action' ] = $start_up_page[ 'action' ] ?? '';
if ($use_template[ 'hope' ] == 'other') {
// 其他页面没有预览图
$use_template[ 'url' ] = $use_template[ 'page' ];
$use_template[ 'cover' ] = ''; // 默认图
$use_template[ 'template' ] = '';
$use_template[ 'mode' ] = 'diy';
$use_template[ 'hope' ] = $use_template[ 'mode' ];
$use_template[ 'desc' ] = '将 ' . $start_up_page[ 'title' ] . ' 设为首页';
}
} else {
// 查询页面数据
$info = $this->getInfoByName($k);
if (!empty($info)) {
$use_template[ 'id' ] = $info[ 'id' ];
$use_template[ 'name' ] = $info[ 'name' ];
$use_template[ 'title' ] = $info[ 'title' ];
$use_template[ 'template' ] = $info[ 'template' ];
$use_template[ 'mode' ] = $info[ 'mode' ];
$use_template[ 'hope' ] = $info[ 'mode' ] == 'fixed' ? 'template' : $info[ 'mode' ];
$use_template[ 'preview' ] = ''; // 默认图
$use_template[ 'desc' ] = '通过自定义装修的页面';
// 查询模板页面数
$page_data = $this->getPageData($k, $use_template[ 'template' ]);
if (!empty($page_data)) {
if ($info[ 'is_change' ] == 1) {
// 修改过模板,预览实际内容
$use_template[ 'url' ] = $v[ 'page' ] . '?id=' . $info[ 'id' ];
} else {
$use_template[ 'cover' ] = $page_data[ 'cover' ]; // 默认图
$use_template[ 'desc' ] = $page_data[ 'desc' ];
if (empty($page_data[ 'cover' ])) {
$use_template[ 'url' ] = $v[ 'page' ] . '?id=' . $info[ 'id' ];
}
}
} else {
// 自定义页面,实时预览效果
$use_template[ 'url' ] = '/app/pages/index/diy?id=' . $info[ 'id' ];
// 清空模板信息
$use_template[ 'cover' ] = ''; // 默认图
$use_template[ 'template' ] = '';
$use_template[ 'mode' ] = 'diy';
$use_template[ 'hope' ] = $use_template[ 'mode' ];
}
}
}
// 如果没有预览图,并且没有地址,则赋值
if (empty($use_template[ 'cover' ]) && empty($use_template[ 'url' ])) {
$use_template[ 'url' ] = $v[ 'page' ];
}
$template[ $k ][ 'use_template' ] = $use_template;
}
return $template;
}
/**
* 切换模板
* @param array $params
* @return array
* @throws Exception
*/
public function changeTemplate(array $params = [])
{
$info = [];
if ($params[ 'mode' ] == 'diy') {
// 自定义页面
@ -481,14 +534,10 @@ class DiyService extends BaseAdminService
// 状态 变为 使用中
$this->setUse($info[ 'id' ]);
}
return $info;
} elseif ($params[ 'template' ]) {
// 查询模板信息
$page_data = $this->getPageData($params[ 'type' ], $params[ 'template' ]);
// 查询表中未修改的模板数据
$field = 'id';
$field = 'id,title,type';
$condition = [
[ 'site_id', '=', $this->site_id ],
[ 'type', '=', $params[ 'type' ] ],
@ -500,9 +549,12 @@ class DiyService extends BaseAdminService
if (!empty($info)) {
// 状态 变为 使用中
$this->setUse($info[ 'id' ]);
return $info;
} else {
// 新增 数据
// 查询模板信息
$page_data = $this->getPageData($params[ 'type' ], $params[ 'template' ]);
$data = [
'title' => $page_data[ 'title' ],
'name' => $params[ 'type' ],
@ -513,10 +565,14 @@ class DiyService extends BaseAdminService
];
$res = $this->add($data);
$this->setUse($res);
$info = $data;
}
}
$page_template = TemplateDict::getTemplate([ 'type' => [ $info[ 'type' ] ] ])[ $info[ 'type' ] ];
$info[ 'page' ] = $page_template[ 'page' ];
} elseif ($params[ 'mode' ] == 'fixed') {
// 固定模板
@ -524,9 +580,8 @@ class DiyService extends BaseAdminService
$page_data = $this->getPageData($params[ 'type' ], $params[ 'template' ]);
// 检查表里是否存在数据
$field = 'id';
$field = 'id,title,type';
$condition = [
[ 'site_id', '=', $this->site_id ],
[ 'type', '=', $params[ 'type' ] ],
[ 'template', '=', $params[ 'template' ] ],
[ 'mode', '=', $params[ 'mode' ] ]
@ -547,10 +602,30 @@ class DiyService extends BaseAdminService
];
$res = $this->add($data);
$this->setUse($res);
$info = $data;
}
return $info;
$page_template = TemplateDict::getTemplate([ 'type' => [ $info[ 'type' ] ] ])[ $info[ 'type' ] ];
$info[ 'page' ] = $page_template[ 'page' ];
} else if ($params[ 'mode' ] == 'other') {
// 其他页面
$info[ 'title' ] = $params[ 'title' ];
$info[ 'page' ] = $params[ 'page' ];
}
return $params;
// 设置启动页
$start_up_page_data = [
'type' => $params[ 'type' ], // 页面类型
'name' => $params[ 'name' ], // 页面名称标识
'mode' => $params[ 'mode' ], // 模式diy自定义页面fixed模板other其他页面
'title' => $info[ 'title' ],
'page' => $info[ 'page' ],
'action' => $params[ 'action' ]
];
$diy_config_service = new DiyConfigService();
$diy_config_service->setStartUpPageConfig($start_up_page_data);
return $info;
}
/**

View File

@ -41,21 +41,20 @@ class CoreAddonDownloadService extends CoreAddonBaseService
* @throws GuzzleException
* @throws GuzzleException
*/
public function download($app_key)
public function download($app_key, $version)
{
$app_path = $this->addon_path . $app_key . '/';
$app_path = $this->addon_path . $app_key . DIRECTORY_SEPARATOR;
//先判断当前的应用在本地是否存在
// if(is_dir($app_path)) throw new NiucloudException();
$app_download_path = $this->addon_download_path . $app_key . '/';
//下载文件到本地
$zip_file = (new CoreModuleService())->downloadModule($app_key, $app_download_path);
$zip_file = (new CoreAddonCloudService())->downloadAddon($app_key, $version);
//解压到应用addon下
//删除旧版本文件
del_target_dir($app_path, true);
//解压文件
$this->unzip($zip_file, $this->addon_path);
//删除压缩包
@unlink($zip_file);
@del_target_dir(dirname($zip_file), true);
return true;
}

View File

@ -79,4 +79,27 @@ class CoreDiyConfigService extends BaseCoreService
return ( new CoreConfigService() )->setConfig($site_id, ConfigKeyDict::DIY_BOTTOM, $data);
}
/**
* 设置启动页
* @param array $data
* @return SysConfig|bool|Model
*/
public function setStartUpPageConfig(int $site_id, array $data)
{
return ( new CoreConfigService() )->setConfig($site_id,'START_UP_PAGE_' . strtoupper($data[ 'type' ]), $data);
}
/**
* 获取启动页配置
* @param $name
* @return array
*/
public function getStartUpPageConfig(int $site_id, string $type)
{
$info = ( new CoreConfigService() )->getConfig($site_id, 'START_UP_PAGE_' . strtoupper($type))[ 'value' ] ?? [];
if (!empty($info)) {
$info[ 'name' ] = isset($info[ 'name' ]) ? $info[ 'name' ] : '';
}
return $info;
}
}

View File

@ -125,6 +125,7 @@ class CoreMenuService extends BaseCoreService
foreach ($tree as $key => $value) {
$item = [
'menu_name' => $value['menu_name'],
'menu_short_name' => $value['menu_short_name'] ?? '',
'menu_key' => $value['menu_key'],
'app_type' => $app_type,
'addon' => $addon,

View File

@ -5,21 +5,21 @@
<link rel="icon" type="image" href="/admin/niucloud.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<script type="module" crossorigin src="/admin/assets/index-acad5537.js"></script>
<script type="module" crossorigin src="/admin/assets/index-e35911a1.js"></script>
<link rel="modulepreload" crossorigin href="/admin/assets/base-9962c822.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-57446bef.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-5c4817f4.js">
<link rel="modulepreload" crossorigin href="/admin/assets/pinia-a9fc3924.js">
<link rel="modulepreload" crossorigin href="/admin/assets/storage-0874d153.js">
<link rel="modulepreload" crossorigin href="/admin/assets/system-1108e5c1.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-15d712d1.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-6e46fb55.js">
<link rel="modulepreload" crossorigin href="/admin/assets/vue-router-d7e63612.js">
<link rel="modulepreload" crossorigin href="/admin/assets/typescript-defaf979.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-c98e204a.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-2cabf788.js">
<link rel="modulepreload" crossorigin href="/admin/assets/_plugin-vue_export-helper-c27b6911.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-c4e33d8d.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-43b4f8e3.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-db1ad0a3.js">
<link rel="modulepreload" crossorigin href="/admin/assets/event-9519ab40.js">
<link rel="modulepreload" crossorigin href="/admin/assets/error-78e43d3e.js">
<link rel="modulepreload" crossorigin href="/admin/assets/scroll-d85c8f38.js">
@ -48,7 +48,7 @@
<link rel="modulepreload" crossorigin href="/admin/assets/index-6d647c6f.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-6edd46fb.js">
<link rel="modulepreload" crossorigin href="/admin/assets/isEqual-240b5e0a.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-8f077450.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-00b73d5a.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-189f302e.js">
<link rel="modulepreload" crossorigin href="/admin/assets/strings-66191554.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-da093955.js">
@ -91,7 +91,7 @@
<link rel="stylesheet" href="/admin/assets/base-a43f0bed.css">
<link rel="stylesheet" href="/admin/assets/index-f5c6a819.css">
<link rel="stylesheet" href="/admin/assets/index-f5128a35.css">
<link rel="stylesheet" href="/admin/assets/index-7accde1d.css">
<link rel="stylesheet" href="/admin/assets/index-4ddae479.css">
</head>
<body>
<div id="app"></div>