This commit is contained in:
wangchen147 2023-11-30 15:31:08 +08:00
parent f2765e9a1f
commit a95521f6af
32 changed files with 668 additions and 156 deletions

View File

@ -0,0 +1,102 @@
<template>
<div class="main-container w-full pt-[64px] bg-white" v-loading="loading">
<div class="flex justify-between items-center h-[32px] mb-4">
<span class="text-[20px]">{{ t('editPersonal') }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="saveInfo" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('headImg')">
<upload-image v-model="saveInfo.head_img" :limit="1" />
</el-form-item>
<el-form-item :label="t('userName')">
<span>{{saveInfo.username}}</span>
</el-form-item>
<el-form-item :label="t('realName')">
<el-input v-model="saveInfo.real_name" :placeholder="t('realNamePlaceholder')" clearable class="input-width" />
</el-form-item>
</el-form>
<div class="flex justify-center mt-[50px]">
<el-button type="primary" @click="submitForm(formRef)">{{ t('save') }}</el-button>
<el-button type="primary" @click="returnFn(formRef)">{{ t('cancel') }}</el-button>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import type { FormInstance, FormRules, ElNotification } from 'element-plus'
import { img } from '@/utils/common'
import { getUserInfo,setUserInfo } from '@/app/api/personal'
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
//
let saveInfo = reactive({
head_img: '',
real_name: '',
username: ''
});
const formRef = ref<FormInstance>();
const loading = ref(true);
/**
* 获取用户信息
*/
const getUserInfoFn = () => {
loading.value = true;
getUserInfo().then(res => {
loading.value = false;
let data = res.data;
saveInfo.head_img = data.head_img;
saveInfo.real_name = data.real_name;
saveInfo.username = data.username;
}).catch(() => {
loading.value = false;
})
}
getUserInfoFn();
const submitForm = (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
formEl.validate((valid) => {
if (valid) {
loading.value = true;
setUserInfo(saveInfo).then((res: any) => {
loading.value = false;
}).catch((err: any) => {
loading.value = false
})
} else {
return false
}
});
}
const returnFn = ()=>{
router.push('/user/center')
}
</script>
<style lang="scss" scoped>
:deep(.personal-body){
background-color: #fff;
.el-form-item__content{
.el-input{
width: 250px;
}
.el-form-item__content{
justify-content: space-between;
}
.el-button{
margin-left: auto;
}
.personal-option{
margin-right: auto;
}
}
}
</style>

View File

@ -15,7 +15,7 @@
<div class="w-[1200px] m-auto mt-[62px]">
<div class="flex justify-between items-center">
<span class="text-[24px] font-bold">站点列表</span>
<el-button type="primary" class="w-[90px] !h-[34px]">创建站点</el-button>
<el-button type="primary" class="w-[90px] !h-[34px]" @click="handleChick">创建站点</el-button>
</div>
<div class="flex justify-between items-center mt-[18px]">
<div class="flex items-center flex-wrap text-[14px] w-[800px]">
@ -142,6 +142,10 @@ const addonList = ref([])
getInstalledAddonList().then(({ data }) => {
addonList.value = data
}).catch()
const handleChick = () => {
ElMessage('加班加点研发中...')
}
</script>
<style lang="scss" scoped>

View File

@ -0,0 +1,96 @@
<template>
<div class="main-container w-full pt-[64px] bg-white" v-loading="loading">
<div class="flex justify-between items-center h-[32px] mb-4">
<span class="text-[20px]">{{ t('personal') }}</span>
<span class="text-[14px] text-[#999] cursor-pointer" @click="toEditPersonal">{{ t('editPersonal') }}</span>
</div>
<el-card class="box-card !border-none" shadow="never">
<el-form :model="saveInfo" label-width="90px" ref="formRef" class="page-form">
<el-form-item :label="t('headImg')">
<el-image class="w-[70px] h-[70px]" :src="img(saveInfo.head_img)" fit="contain">
<template #error>
<div class="image-slot bg-[#c0c4cc] flex items-center justify-center w-[70px] h-[70px] rounded-full">
<el-icon class="!text-[#fff] !text-[45px]"><UserFilled /></el-icon>
</div>
</template>
</el-image>
</el-form-item>
<el-form-item :label="t('userName')">
<div>{{saveInfo.username}}</div>
</el-form-item>
<el-form-item :label="t('realName')">
<div>{{saveInfo.real_name}}</div>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import type { FormInstance, FormRules, ElNotification } from 'element-plus'
import { img } from '@/utils/common'
import { getUserInfo,setUserInfo } from '@/app/api/personal'
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
//
let saveInfo = reactive({
head_img: '',
real_name: '',
original_password: '',
password: '',
password_copy: '',
username: ''
});
const formRef = ref<FormInstance>();
const loading = ref(true);
/**
* 获取用户信息
*/
const getUserInfoFn = () => {
loading.value = true;
getUserInfo().then(res => {
loading.value = false;
let data = res.data;
saveInfo.head_img = data.head_img;
saveInfo.real_name = data.real_name;
saveInfo.original_password = data.original_password;
saveInfo.password = data.password;
saveInfo.password_copy = data.password;
saveInfo.username = data.username;
}).catch(() => {
loading.value = false;
})
}
getUserInfoFn();
//
const toEditPersonal = () => {
router.push('/user/edit_center')
}
</script>
<style lang="scss" scoped>
:deep(.personal-body){
background-color: #fff;
.el-form-item__content{
.el-input{
width: 250px;
}
.el-form-item__content{
justify-content: space-between;
}
.el-button{
margin-left: auto;
}
.personal-option{
margin-right: auto;
}
}
}
</style>

View File

@ -81,29 +81,29 @@
</el-row>
</el-card>
<div class="flex justify-between mt-[15px]">
<div class="w-[258px] h-[132px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer"
<div class="flex-1 h-[145px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer mr-[25px]"
@click="toLink('site/list')">
<img class="w-[52px]" src="@/app/assets/images/index/site1.png" />
<span class="text-[16px] text-[#333]">{{ t("siteList") }}</span>
</div>
<div class="w-[258px] h-[132px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer"
<div class="flex-1 h-[145px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer mr-[25px]"
@click="toLink('site/group')">
<img class="w-[52px]" src="@/app/assets/images/index/site_class1.png" />
<span class="text-[16px] text-[#333]">{{ t("sitePackage") }}</span>
</div>
<div class="w-[258px] h-[132px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer"
<div class="flex-1 h-[145px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer mr-[25px]"
@click="toLink('site/list')">
<img class="w-[52px]" src="@/app/assets/images/index/new_site1.png" />
<span class="text-[16px] text-[#333]">{{ t("newSite") }}</span>
</div>
<div class="w-[258px] h-[132px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer"
<div class="flex-1 h-[145px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer mr-[25px]"
@click="toLink('/admin/site/user')">
<img class="w-[52px]" src="@/app/assets/images/index/auth1.png" />
<span class="text-[16px] text-[#333]">{{
t("administrator")
}}</span>
</div>
<div class="w-[258px] h-[132px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer"
<div class="flex-1 h-[145px] bg-[#F9F9F9] flex justify-center flex-col items-center cursor-pointer"
@click="toApplication">
<img class="w-[52px]" src="@/app/assets/images/index/app1.png" />
<span class="text-[16px] text-[#333]">{{
@ -187,7 +187,7 @@ let statInfo = ref({
about: [],
site_stat: {},
site_group_stat: {},
app: {},
app: {}
});
const getStatInfoFn = async (id: number = 0) => {
statInfo.value = await (await getStatInfo()).data;
@ -304,8 +304,7 @@ nowTime()
<style lang="scss" scoped>
.main-container {
width: 1428px;
margin: 0 auto;
margin: 0 84px;
}
:deep(.profile-data .el-card__header) {

View File

@ -294,7 +294,7 @@
</div>
</div>
<div v-show="installStep == 2" class="h-[50vh] mt-[20px]">
<terminal :name="currAddon" :context="currAddon" :init-log="null" :show-header="false"
<terminal ref="terminalRef" :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">
@ -399,6 +399,7 @@ const downloading = ref('')
const installAfterTips = ref<string[]>([])
const userStore = useUserStore()
const unloadHintDialog = ref(false)
const terminalRef = ref(null)
const currDownData = ref()
const downEventHintFn = () => {
@ -635,8 +636,8 @@ const handleCloudInstall = () => {
cloudInstallAddon({ addon: currAddon.value }).then(res => {
installStep.value = 2
terminalApi.execute(currAddon.value, 'clear')
terminalApi.pushMessage(currAddon.value, { content: '开始安装插件', class: 'info' })
terminalRef.value.execute('clear')
terminalRef.value.pushMessage({ content: '开始安装插件', class: 'info' })
getInstallTask()
cloudInstalling.value = false
}).catch((res) => {
@ -670,11 +671,11 @@ const getCloudInstallLog = () => {
if (data[0] && data[0].length && installShowDialog.value == true) {
data[0].forEach(item => {
if (!installLog.includes(item.action)) {
terminalApi.pushMessage(currAddon.value, { content: `正在执行:${item.action}` })
terminalRef.value.pushMessage({ content: `正在执行:${item.action}` })
installLog.push(item.action)
if (item.code == 0) {
terminalApi.pushMessage(currAddon.value, { content: item.msg, class: 'error' })
terminalRef.value.pushMessage({ content: item.msg, class: 'error' })
}
}
})

View File

@ -0,0 +1,101 @@
<template>
<div class="main-container w-full bg-white" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<el-form :model="saveInfo" label-width="80px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('headImg')">
<upload-image v-model="saveInfo.head_img" :limit="1" />
</el-form-item>
<el-form-item :label="t('userName')">
<span>{{saveInfo.username}}</span>
</el-form-item>
<el-form-item :label="t('realName')">
<el-input v-model="saveInfo.real_name" :placeholder="t('realNamePlaceholder')" clearable class="input-width" />
</el-form-item>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="submitForm(formRef)">{{ t('save') }}</el-button>
<el-button type="primary" @click="returnFn(formRef)">{{ t('cancel') }}</el-button>
</div>
</div>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import type { FormInstance, FormRules, ElNotification } from 'element-plus'
import { img } from '@/utils/common'
import { getUserInfo,setUserInfo } from '@/app/api/personal'
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
//
let saveInfo = reactive({
head_img: '',
real_name: '',
username: ''
});
const formRef = ref<FormInstance>();
const loading = ref(true);
/**
* 获取用户信息
*/
const getUserInfoFn = () => {
loading.value = true;
getUserInfo().then(res => {
loading.value = false;
let data = res.data;
saveInfo.head_img = data.head_img;
saveInfo.real_name = data.real_name;
saveInfo.username = data.username;
}).catch(() => {
loading.value = false;
})
}
getUserInfoFn();
const submitForm = (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
formEl.validate((valid) => {
if (valid) {
loading.value = true;
setUserInfo(saveInfo).then((res: any) => {
loading.value = false;
}).catch((err: any) => {
loading.value = false
})
} else {
return false
}
});
}
const returnFn = ()=>{
router.push('/user/center')
}
</script>
<style lang="scss" scoped>
:deep(.personal-body){
background-color: #fff;
.el-form-item__content{
.el-input{
width: 250px;
}
.el-form-item__content{
justify-content: space-between;
}
.el-button{
margin-left: auto;
}
.personal-option{
margin-right: auto;
}
}
}
</style>

View File

@ -0,0 +1,93 @@
<template>
<div class="main-container w-full bg-white" v-loading="loading">
<el-card class="box-card !border-none relative" shadow="never">
<el-form :model="saveInfo" label-width="80px" ref="formRef" class="page-form">
<el-form-item :label="t('headImg')">
<el-image class="w-[70px] h-[70px]" :src="img(saveInfo.head_img)" fit="contain">
<template #error>
<div class="image-slot bg-[#c0c4cc] flex items-center justify-center w-[70px] h-[70px] rounded-full">
<el-icon class="!text-[#fff] !text-[45px]"><UserFilled /></el-icon>
</div>
</template>
</el-image>
</el-form-item>
<el-form-item :label="t('userName')">
<div>{{saveInfo.username}}</div>
</el-form-item>
<el-form-item :label="t('realName')">
<div>{{saveInfo.real_name}}</div>
</el-form-item>
</el-form>
<span class="text-[14px] text-[#999] absolute top-[25px] right-[20px] cursor-pointer" @click="toEditPersonal">{{ t('editPersonal') }}</span>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import type { FormInstance, FormRules, ElNotification } from 'element-plus'
import { img } from '@/utils/common'
import { getUserInfo,setUserInfo } from '@/app/api/personal'
import { useRoute, useRouter } from 'vue-router'
const router = useRouter()
//
let saveInfo = reactive({
head_img: '',
real_name: '',
original_password: '',
password: '',
password_copy: '',
username: ''
});
const formRef = ref<FormInstance>();
const loading = ref(true);
/**
* 获取用户信息
*/
const getUserInfoFn = () => {
loading.value = true;
getUserInfo().then(res => {
loading.value = false;
let data = res.data;
saveInfo.head_img = data.head_img;
saveInfo.real_name = data.real_name;
saveInfo.original_password = data.original_password;
saveInfo.password = data.password;
saveInfo.password_copy = data.password;
saveInfo.username = data.username;
}).catch(() => {
loading.value = false;
})
}
getUserInfoFn();
//
const toEditPersonal = () => {
router.push('/user/edit_center')
}
</script>
<style lang="scss" scoped>
:deep(.personal-body){
background-color: #fff;
.el-form-item__content{
.el-input{
width: 250px;
}
.el-form-item__content{
justify-content: space-between;
}
.el-button{
margin-left: auto;
}
.personal-option{
margin-right: auto;
}
}
}
</style>

View File

@ -59,7 +59,7 @@
<el-table :data="detail.roles" size="large">
<el-table-column prop="site_id" :label="t('siteId')" width="100px" />
<el-table-column prop="site_name" :label="t('siteName')" />
<el-table-column prop="last_time" :label="t('isAdmin')" min-width="180" align="center">
<el-table-column prop="is_admin" :label="t('isAdmin')" min-width="180" align="center">
<template #default="{ row }">
{{ row.is_admin ? t('yes') : t('no') }}
</template>

View File

@ -1,7 +1,7 @@
<template>
<div class="main-container mb-80" v-loading="loading">
<div class="detail-head !mb-[10px]">
<div class="left" @click="router.push({ path: '/setting/tools/addon' })">
<div class="left" @click="router.push({ path: '/tools/addon' })">
<span class="iconfont iconxiangzuojiantou !text-xs"></span>
<span class="ml-[1px]">{{ t('returnToPreviousPage') }}</span>
</div>
@ -226,7 +226,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
setTimeout(() => {
window.addonActiveName = 'pluginList'
router.push({ path: "/setting/tools/addon" })
router.push({ path: "/tools/addon" })
}, 650)
}).catch(() => {

View File

@ -29,7 +29,7 @@
<template #description>
<span class="text-[#999]">{{ t("describe1") }}</span>
<div class="mt-[20px] mb-[40px] h-[32px]">
<el-button type="primary" @click="router.push({ path: '/setting/tools/addon_edit' })">{{t("btn1") }}</el-button>
<el-button type="primary" @click="router.push({ path: '/tools/addon_edit' })">{{t("btn1") }}</el-button>
</div>
</template>
</el-step>
@ -248,7 +248,7 @@ const resetForm = (formEl: FormInstance | undefined) => {
getAddonDevelopFn();
}
const editEvent = (key: any) => {
router.push({ path: '/setting/tools/addon_edit', query: { key } })
router.push({ path: '/tools/addon_edit', query: { key } })
}
const linkEvent = (url: string) => {
window.open(url, "_blank")

View File

@ -25,14 +25,14 @@
<icon name="iconfont-iconqiehuan" :title="t('indexSwitch')"/>
</div>
<!-- 切换语言 -->
<div class="navbar-item flex items-center h-full cursor-pointer">
<!-- <div class="navbar-item flex items-center h-full cursor-pointer">
<switch-lang />
</div>
</div> -->
<!-- 切换全屏 -->
<div class="navbar-item flex items-center h-full cursor-pointer" @click="toggleFullscreen">
<!-- <div class="navbar-item flex items-center h-full cursor-pointer" @click="toggleFullscreen">
<icon name="iconfont-icontuichuquanping" v-if="isFullscreen" />
<icon name="iconfont-iconquanping" v-else />
</div>
</div> -->
<!-- 布局设置 -->
<!-- <div class="navbar-item flex items-center h-full cursor-pointer">
<layout-setting />
@ -145,26 +145,6 @@ onMounted(() => {
}
})
watch(screenWidth, () => {
if (screenWidth.value < 992) {
if (!systemStore.menuIsCollapse) systemStore.toggleMenuCollapse(true)
} else {
if (systemStore.menuIsCollapse) systemStore.toggleMenuCollapse(false)
}
})
//
const toggleMenuCollapse = () => {
systemStore.$patch((state) => {
if (screenWidth.value < 768) {
state.menuDrawer = true
state.menuIsCollapse = false
} else {
systemStore.toggleMenuCollapse(!systemStore.menuIsCollapse)
}
})
}
//
const refreshRouter = () => {
if (!appStore.routeRefreshTag) return

View File

@ -8,10 +8,34 @@
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item><router-link to="/user/center">账号设置</router-link></el-dropdown-item>
<el-dropdown-item><router-link to="/tools/authorize">授权信息</router-link></el-dropdown-item>
<el-dropdown-item @click="changePasswordDialog=true">修改密码</el-dropdown-item>
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
<el-dropdown-item>
<router-link to="/user/center">
<div class="flex items-center leading-[1] py-[5px]">
<span class="iconfont iconshezhi1 ml-[4px] !text-[14px] mr-[10px]"></span>
<span class="text-[14px]">账号设置</span>
</div>
</router-link>
</el-dropdown-item>
<el-dropdown-item>
<router-link to="/tools/authorize">
<div class="flex items-center leading-[1] py-[5px]">
<span class="iconfont iconshouquanxinxi2 ml-[4px] !text-[14px] mr-[10px]"></span>
<span class="text-[14px]">授权信息</span>
</div>
</router-link>
</el-dropdown-item>
<el-dropdown-item @click="changePasswordDialog=true">
<div class="flex items-center leading-[1] py-[5px]">
<span class="iconfont iconxiugai ml-[4px] !text-[14px] mr-[10px]"></span>
<span class="text-[14px]">修改密码</span>
</div>
</el-dropdown-item>
<el-dropdown-item @click="logout">
<div class="flex items-center leading-[1] py-[5px]">
<span class="iconfont icontuichudenglu ml-[4px] !text-[14px] mr-[10px]"></span>
<span class="text-[14px]">退出登录</span>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -102,8 +126,6 @@ const submitForm = (formEl: FormInstance | undefined) => {
setUserInfo(saveInfo).then((res: any) => {
changePasswordDialog.value = false;
}).catch((err: any) => {
changePasswordDialog.value = false;
})
} else {
return false
@ -113,4 +135,8 @@ const submitForm = (formEl: FormInstance | undefined) => {
// --- end
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.el-popper .el-dropdown-menu{
width: 150px;
}
</style>

View File

@ -24,19 +24,15 @@
<div class="right-panel h-full flex items-center justify-end">
<!-- 预览 只有站点时展示-->
<i class="iconfont iconlingdang-xianxing cursor-pointer px-[8px]" :title="t('newInfo')" v-if="appType == 'site'"></i>
<!-- 切换首页 -->
<div class="navbar-item flex items-center h-full cursor-pointer" v-if="appType == 'site'" @click="checkIndexList">
<icon name="iconfont-iconqiehuan" :title="t('indexSwitch')"/>
</div>
<!-- 切换语言 -->
<div class="navbar-item flex items-center h-full cursor-pointer">
<!-- <div class="navbar-item flex items-center h-full cursor-pointer">
<switch-lang />
</div>
</div> -->
<!-- 切换全屏 -->
<div class="navbar-item flex items-center h-full cursor-pointer" @click="toggleFullscreen">
<!-- <div class="navbar-item flex items-center h-full cursor-pointer" @click="toggleFullscreen">
<icon name="iconfont-icontuichuquanping" v-if="isFullscreen" />
<icon name="iconfont-iconquanping" v-else />
</div>
</div> -->
<!-- 布局设置 -->
<div class="navbar-item flex items-center h-full cursor-pointer">
<layout-setting />
@ -59,26 +55,6 @@
</span>
</template>
</el-dialog>
<el-dialog v-model="showDialog" :title="t('indexTemplate')" width="550px" :destroy-on-close="true" >
<div class="flex flex-wrap">
<div v-for="(items, index) in indexList" :key="index" v-if="index_path == ''">
<div @click="index_path = items.view_path" class="index-item py-[5px] px-[10px] mr-[10px] rounded-[3px] cursor-pointer" :class="items.is_use == 1 ? 'bg-primary text-[#fff]' : '' ">
<span >{{ items.name }}</span>
</div>
</div>
<div v-for="(itemTo, indexTo) in indexList" :key="indexTo" v-else>
<div @click="index_path = itemTo.view_path" class="index-item py-[5px] px-[10px] mr-[10px] rounded-[3px] cursor-pointer" :class="index_path == itemTo.view_path ? 'bg-primary text-[#fff]' : '' ">
<span >{{ itemTo.name }}</span>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type="primary" @click="submitIndex">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</el-container>
</template>
@ -93,7 +69,6 @@ import useAppStore from '@/stores/modules/app'
import { useRoute, useRouter } from 'vue-router'
import { t } from '@/lang'
import storage from '@/utils/storage'
import { getIndexList, setIndexList } from '@/app/api/sys'
const router = useRouter()
const appType = storage.get('app_type')
@ -179,29 +154,6 @@ const backFn = () => {
router.go(-1)
}
const indexList = ref();
const showDialog = ref(false)
const checkIndexList = () => {
getIndexList().then(res => {
showDialog.value = true
indexList.value = res.data
for(let i = 0 ; i < indexList.value.length; i ++){
if(indexList.value[i].is_use == 1){
index_path.value = indexList.value[i].view_path
}
}
})
}
const index_path = ref('');
const submitIndex = () => {
setIndexList({
view_path: index_path.value
}).then(() => {
showDialog.value = false
router.go(0)
})
}
</script>
<style lang="scss" scoped>

View File

@ -1,33 +1,74 @@
<template>
<el-dropdown @command="clickEvent" :tabindex="1">
<div class="userinfo flex h-full items-center">
<el-avatar :size="25" :icon="UserFilled" />
<div class="user-name pl-[8px]">{{ userStore.userInfo.username }}</div>
<icon name="element-ArrowDown" class="ml-[5px]" />
</div>
<template #dropdown>
<el-dropdown-menu>
<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>
<div>
<el-dropdown @command="clickEvent" :tabindex="1">
<div class="userinfo flex h-full items-center">
<el-avatar :size="25" :icon="UserFilled" />
<div class="user-name pl-[8px]">{{ userStore.userInfo.username }}</div>
<icon name="element-ArrowDown" class="ml-[5px]" />
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="toLink('/home/index')">
<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 @click="toLink('/user/center')">
<div class="flex items-center leading-[1] py-[5px]">
<span class="iconfont iconshezhi1 ml-[4px] !text-[14px] mr-[10px]"></span>
<span class="text-[14px]">账号设置</span>
</div>
</el-dropdown-item>
<el-dropdown-item @click="changePasswordDialog=true">
<div class="flex items-center leading-[1] py-[5px]">
<span class="iconfont iconxiugai 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>
<el-dialog v-model="changePasswordDialog" width="450px" title="修改密码" :before-close="handleClose">
<div>
<el-form :model="saveInfo" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('originalPassword')" prop="original_password">
<el-input v-model="saveInfo.original_password" type="password" :placeholder="t('originalPasswordPlaceholder')" clearable class="input-width" />
</el-form-item>
<el-form-item :label="t('newPassword')" prop="password">
<el-input v-model="saveInfo.password" type="password" :placeholder="t('passwordPlaceholder')" clearable class="input-width" />
<div class="form-tip">{{t('passwordTip')}}</div>
</el-form-item>
<el-form-item :label="t('passwordCopy')" prop="password_copy">
<el-input v-model="saveInfo.password_copy" type="password" :placeholder="t('passwordPlaceholder')" clearable class="input-width" />
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="changePasswordDialog = false">{{t('cancel')}}</el-button>
<el-button type="primary" @click="submitForm(formRef)">{{t('save')}}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { CollectionTag, UserFilled } from '@element-plus/icons-vue'
import { computed, reactive, ref, onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import type { FormInstance, FormRules, ElNotification } from 'element-plus'
import useUserStore from '@/stores/modules/user'
import { useRouter } from 'vue-router'
import { setUserInfo } from '@/app/api/personal'
import { t } from '@/lang'
const userStore = useUserStore()
const router = useRouter()
@ -40,9 +81,57 @@ const clickEvent = (command: string) => {
}
}
const toSiteLink = ()=>{
router.push('/home/index');
const toLink = (link)=>{
router.push(link);
}
// --- start
let changePasswordDialog = ref(false)
const formRef = ref<FormInstance>();
//
let saveInfo = reactive({
original_password: '',
password: '',
password_copy: ''
});
//
const formRules = reactive<FormRules>({
original_password: [
{ required: true, message: t("originalPasswordPlaceholder"), trigger: "blur" },
],
password: [
{ required: true, message: t("passwordPlaceholder"), trigger: "blur" },
],
password_copy: [
{ required: true, message: t("passwordPlaceholder"), trigger: "blur" },
]
});
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.validate((valid) => {
if (valid) {
let msg = "";
if (saveInfo.password && !saveInfo.original_password) msg = t('originalPasswordHint');
if (saveInfo.password && saveInfo.original_password && !saveInfo.password_copy) msg = t('newPasswordHint');
if (saveInfo.password && saveInfo.original_password && saveInfo.password_copy && saveInfo.password != saveInfo.password_copy) msg = t('doubleCipherHint');
if (msg) {
ElNotification({
type: 'error',
message: msg,
})
return;
}
setUserInfo(saveInfo).then((res: any) => {
changePasswordDialog.value = false;
})
} else {
return false
}
});
}
// --- end
</script>
<style lang="scss" scoped>

View File

@ -109,7 +109,21 @@ export const SITE_ROUTE: RouteRecordRaw = {
type: 1,
title: '个人中心'
},
component: () => import('@/app/views/index/personal.vue')
component: () => import('@/app/views/site/personal.vue')
}
]
},
{
path: 'user',
component: Default,
children: [
{
path: 'edit_center',
meta: {
type: 1,
title: '编辑个人中心'
},
component: () => import('@/app/views/site/edit_personal.vue')
}
]
}

View File

@ -172,6 +172,18 @@ class Config extends BaseAdminController
return success();
}
/**
* 获取手机端首页列表
*/
public function getWapIndexList()
{
$data = $this->request->params([
[ 'title', '' ],
[ 'key', '' ] // 多个查询,逗号隔开
]);
return success(( new ConfigService() )->getWapIndexList($data));
}
/**
* 设置快捷菜单
*/

View File

@ -86,6 +86,9 @@ Route::group('sys', function () {
//获取平台首页加载
Route::get('config/admin_index', 'sys.Config/getAdminIndexList');
// 获取手机端首页加载
Route::get('config/wap_index', 'sys.Config/getWapIndexList');
//快捷菜单设置
Route::put('config/shortcut_menu', 'sys.Config/setShortcutMenu');
//获取快捷菜单

View File

@ -861,7 +861,7 @@ return
'menu_name' => '注册登录',
'menu_key' => 'setting_login_register',
'menu_type' => 1,
'icon' => 'iconfont-icondenglu',
'icon' => 'iconfont-iconzhuceshezhi',
'api_url' => 'member/config/login',
'router_path' => 'login',
'view_path' => 'setting/login',

View File

@ -116,7 +116,7 @@ class Site extends BaseModel
return $this->hasOne(SiteGroup::class, 'group_id', 'group_id')->joinType('left')->withField('group_id, group_name')->bind(['group_name' => 'group_name']);
}
public function addon() {
public function addonName() {
return $this->hasOne(Addon::class, 'key', 'app')->bind(['app_name' => 'title']);
}

View File

@ -80,7 +80,7 @@ class AuthSiteService extends BaseAdminService
{
$field = 'site_id, site_name, front_end_name, front_end_logo, app_type, keywords, logo, icon, `desc`, status, latitude, longitude, province_id, city_id,
district_id, address, full_address, phone, business_hours, create_time, expire_time, group_id, app';
$search_model = $this->model->where([['site_id', 'in', $this->getSiteIds()]])->withSearch([ 'create_time', 'expire_time', 'keywords', 'status', 'group_id', 'app' ], $where)->with(['groupName', 'addon'])->field($field)->append([ 'status_name' ])->order('create_time desc');
$search_model = $this->model->where([['site_id', 'in', $this->getSiteIds()]])->withSearch([ 'create_time', 'expire_time', 'keywords', 'status', 'group_id', 'app' ], $where)->with(['groupName', 'addonName'])->field($field)->append([ 'status_name' ])->order('create_time desc');
return $this->pageQuery($search_model);
}

View File

@ -60,7 +60,7 @@ class SiteService extends BaseAdminService
$condition = [
[ 'app_type', '<>', 'admin' ]
];
$search_model = $this->model->where($condition)->withSearch([ 'create_time', 'expire_time', 'keywords', 'status', 'group_id', 'app' ], $where)->with(['groupName', 'addon'])->field($field)->append([ 'status_name' ])->order('create_time desc');
$search_model = $this->model->where($condition)->withSearch([ 'create_time', 'expire_time', 'keywords', 'status', 'group_id', 'app' ], $where)->with(['groupName', 'addonName'])->field($field)->append([ 'status_name' ])->order('create_time desc');
return $this->pageQuery($search_model);
}
@ -73,8 +73,12 @@ class SiteService extends BaseAdminService
{
$field = 'site_id, site_name, front_end_name, front_end_logo, app_type, keywords, logo, icon, `desc`, status, latitude, longitude, province_id, city_id,
district_id, address, full_address, phone, business_hours, create_time, expire_time, group_id, app, addons';
return $this->model->where([ [ 'site_id', '=', $site_id ] ])->with('groupName')->field($field)->append([ 'status_name' ])->findOrEmpty()->toArray();
$info = $this->model->where([ [ 'site_id', '=', $site_id ] ])->with([ 'groupName', 'addonName' ])->field($field)->append([ 'status_name' ])->findOrEmpty()->toArray();
if (!empty($info)) {
$site_addons = (new CoreSiteService())->getAddonKeysBySiteId($site_id);
$info['site_addons'] = (new Addon())->where([ ['key', 'in', $site_addons]])->field('key,title,desc,icon')->select()->toArray();
}
return $info;
}
/**
@ -174,7 +178,7 @@ class SiteService extends BaseAdminService
$where = [
[ 'site_id', '=', $site_id ],
];
$site = $this->model->where($where)->field('site_id, app_type,site_name,front_end_name,front_end_logo,logo,icon,group_id, status, expire_time, addons')->append([ 'status_name' ])->findOrEmpty();
$site = $this->model->where($where)->field('site_id, app_type,site_name,front_end_name,front_end_logo,logo,icon,group_id, status, expire_time, app, addons')->append([ 'status_name' ])->findOrEmpty();
return $site->toArray();
},
self::$cache_tag_name . $site_id

View File

@ -86,9 +86,9 @@ class SiteUserService extends BaseAdminService
$query->field('uid, site_id, is_admin')->with('siteInfo');
}])->findOrEmpty()->toArray();
if (!empty($info)) {
$info['roles'] = array_filter(array_map(function ($item) {
$info['roles'] = array_values(array_filter(array_map(function ($item) {
if ($item['site_id']) return $item;
}, $info['roles']));
}, $info['roles'])));
}
return $info;
}

View File

@ -360,4 +360,14 @@ class ConfigService extends BaseAdminService
}
return $index_list;
}
}
/**
* 获取手机端首页列表
* @param $data
* @return array
*/
public function getWapIndexList($data)
{
return ( new CoreSysConfigService() )->getWapIndexList($data);
}
}

View File

@ -14,6 +14,7 @@ namespace app\service\core\site;
use app\dict\site\SiteDict;
use app\dict\sys\AppTypeDict;
use app\model\site\Site;
use app\model\site\SiteGroup;
use app\service\admin\site\SiteGroupService;
use app\service\admin\site\SiteService;
use core\base\BaseCoreService;
@ -110,20 +111,23 @@ class CoreSiteService extends BaseCoreService
* @return array
*/
public function getAddonKeysBySiteId(int $site_id){
$site_info = (new SiteService())->getSiteCache($site_id);
if (empty($site_info))
return [];
$site_info = (new Site())->where([ ['site_id', '=', $site_id] ])->field('group_id,app_type,addons')->findOrEmpty();
if ($site_info->isEmpty()) return [];
$app_type = $site_info[ 'app_type' ];
$group_addon_keys = [];
if ($app_type == AppTypeDict::SITE) {
$group_id = $site_info[ 'group_id' ] ?? 0;
if ($group_id > 0) {
$group_addon_keys = ( new SiteGroupService() )->getGroupAddon($group_id);
} else {
$group_addon_keys = [];
$group = (new SiteGroup())->field('app,addon')->findOrEmpty($group_id);
if (!$group->isEmpty()) {
$group_addon_keys = array_merge([ $group['app'] ], $group['addon']);
}
}
}
//在查询站点所拥有的应用插件,两者结合
$site_addon_keys = $site_info['addons'] ?? [];
$site_addon_keys = is_array($site_info['addons']) ? $site_info['addons'] : [];
return array_merge($group_addon_keys ?? [], $site_addon_keys);
}
}

View File

@ -64,4 +64,28 @@ class CoreSysConfigService extends BaseCoreService
}
return $info['value'];
}
}
/**
* 获取手机端首页列表
* @param array $data
* @return array
*/
public function getWapIndexList($data = [])
{
$result = event("WapIndex");
$index_list = [];
foreach ($result as $v) {
$index_list = empty($index_list) ? $v : array_merge($index_list, $v);
}
foreach ($index_list as $k => $v) {
if (!empty($data[ 'key' ]) && !in_array($v[ 'key' ], explode(',', $data[ 'key' ]))) {
unset($index_list[ $k ]);
continue;
}
}
$index_list = array_values($index_list);
return $index_list;
}
}

View File

@ -1,6 +1,6 @@
<?php
return [
'version' => '0.0.3',
'code' => '202311250001'
'version' => '0.0.4',
'code' => '202311300001'
];

View File

@ -67,6 +67,7 @@ trait AccessToken
{
$access_token_info = $this->httpGet('auth', ['code' => $this->code, 'secret' => $this->secret, 'token' => $this->createToken(), 'product_key' => self::PRODUCT, 'redirect_uri' => $this->getDomain(false)]);
if (isset($access_token_info['code']) && $access_token_info['code'] != 1) throw new NiucloudException($access_token_info['msg']);
$this->setAccessToken($access_token_info['data']['token']);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1 +0,0 @@
const e=""+new URL("default_headimg-1e927329.png",import.meta.url).href;export{e as _};

View File

@ -1 +0,0 @@
.horz-blank-slider .el-slider__input{width:100px}

View File

@ -1 +0,0 @@
const e=""+new URL("default_headimg-1e927329.png",import.meta.url).href;export{e as _};

View File

@ -5,7 +5,7 @@
<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-e35911a1.js"></script>
<script type="module" crossorigin src="/admin/assets/index-32be3ace.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">
@ -19,7 +19,7 @@
<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-db1ad0a3.js">
<link rel="modulepreload" crossorigin href="/admin/assets/index-38fee8c5.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">
@ -91,10 +91,10 @@
<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-4ddae479.css">
<link rel="stylesheet" href="/admin/assets/index-f45d3e43.css">
</head>
<body>
<div id="app"></div>
</body>
</html>