智慧教务系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

389 lines
12 KiB

<template>
<div class="main-container">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-card
class="box-card mt-[15px] !border-none"
shadow="never"
v-loading="loading"
>
<div class="flex">
<div
class="w-[360px] h-[400px] absolute mr-[30px] border-[1px] border-gray-300"
>
<div
class="flex items-center justify-between absolute h-[60px] left-[0px] right-[0px] bottom-[0px] border-[1px] border-primary"
:style="{ backgroundColor: diyBottomData.value.backgroundColor }"
>
<div
class="flex flex-1 flex-col items-center justify-center"
v-for="(item, index) in diyBottomData.value.list"
:key="'b' + index"
>
<el-image
class="w-[22px] h-[22px] mb-[5px] leading-1"
:src="img(item.iconPath)"
:fit="contain"
v-if="['1', '2'].includes(diyBottomData.value.type.toString())"
>
<template #error>
<div class="image-slot flex justify-center items-center mt-1">
<el-icon
><Picture class="text-3xl text-gray-500"
/></el-icon>
</div>
</template>
</el-image>
<span
class="text-[12px]"
v-if="['1', '3'].includes(diyBottomData.value.type.toString())"
:style="{ color: diyBottomData.value.textColor }"
>{{ item.text }}</span
>
</div>
</div>
</div>
<div class="flex-1 ml-[430px]">
<div
class="flex items-center border-l-[3px] border-primary pl-[5px] leading-[1.1] mt-[10px]"
>
<span class="text-[14px]">{{ t('editing') }}</span>
<span class="text-[14px] text-primary mx-[3px]">{{
diyBottomData.info.title
}}</span>
<span class="text-[14px]">{{ t('bottomNav') }}</span>
<span class="text-[12px] ml-[8px] text-gray-500">{{
t('bottomNavHint')
}}</span>
</div>
<el-form
:model="diyBottomData.value"
label-width="100px"
ref="formRef"
>
<el-tabs v-model="activeName" class="demo-tabs mt-[15px]">
<el-tab-pane :label="t('navImage')" name="navPicture">
<div ref="navItemRef">
<div
v-for="(item, index) in diyBottomData.value.list"
:key="'a' + index"
:data-id="index"
class="item-wrap border-2 border-dashed pt-[18px] m-[10px] mb-[15px] relative list-item"
:class="{ 'not-sort': useDrag }"
>
<el-form-item :label="t('navIconOne')">
<div class="flex align-center">
<div class="flex flex-col justify-center items-center">
<upload-image
v-model="item.iconPath"
width="60px"
height="60px"
:limit="1"
/>
<span class="mr-[10px] text-sm">{{
t('uploadImgUnselected')
}}</span>
</div>
<div class="flex flex-col justify-center items-center">
<upload-image
v-model="item.iconSelectPath"
width="60px"
height="60px"
:limit="1"
/>
<span class="mr-[10px] text-sm">{{
t('uploadImgSelected')
}}</span>
</div>
</div>
</el-form-item>
<el-form-item :label="t('navTitleOne')">
<el-input
class="!w-[215px]"
v-model.trim="item.text"
:placeholder="t('titleContent')"
maxlength="5"
show-word-limit
/>
</el-form-item>
<el-form-item :label="t('navLinkOne')">
<diy-link v-model="item.link" @confirm="diyLinkFn" />
</el-form-item>
<el-icon
class="close-icon cursor-pointer -top-[11px] -right-[8px]"
@click="deleteNav(index)"
>
<CircleCloseFilled />
</el-icon>
</div>
</div>
<el-button
type="primary"
class="mt-[15px]"
v-show="diyBottomData.value.list.length < 5"
@click="addNav"
>{{ t('addnav') }}</el-button
>
</el-tab-pane>
<el-tab-pane :label="t('styleSet')" name="setStyle">
<el-form-item :label="t('navType')">
<el-radio-group
v-model="diyBottomData.value.type"
class="ml-4"
>
<el-radio label="1" size="large">{{
t('imageText')
}}</el-radio>
<el-radio label="2" size="large">{{ t('image') }}</el-radio>
<el-radio label="3" size="large">{{ t('text') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('textColor')">
<div class="flex align-center">
<el-color-picker v-model="diyBottomData.value.textColor" />
<el-input
class="ml-[10px]"
v-model.trim="diyBottomData.value.textColor"
disabled
/>
<el-button
class="ml-[10px]"
type="primary"
@click="diyBottomData.value.textColor = '#333333'"
>{{ t('reset') }}</el-button
>
</div>
</el-form-item>
<el-form-item :label="t('textSelectColor')">
<div class="flex align-center">
<el-color-picker
v-model="diyBottomData.value.textHoverColor"
/>
<el-input
class="ml-[10px]"
v-model.trim="diyBottomData.value.textHoverColor"
disabled
/>
<el-button
class="ml-[10px]"
type="primary"
@click="diyBottomData.value.textHoverColor = '#333333'"
>{{ t('reset') }}</el-button
>
</div>
</el-form-item>
<el-form-item :label="t('backgroundColor')">
<div class="flex align-center">
<el-color-picker
v-model="diyBottomData.value.backgroundColor"
/>
<el-input
class="ml-[10px]"
v-model.trim="diyBottomData.value.backgroundColor"
disabled
/>
<el-button
class="ml-[10px]"
type="primary"
@click="diyBottomData.value.backgroundColor = '#FFFFFF'"
>{{ t('reset') }}</el-button
>
</div>
</el-form-item>
</el-tab-pane>
</el-tabs>
</el-form>
</div>
</div>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{
t('save')
}}</el-button>
<el-button @click="back()">{{ t('back') }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, nextTick } from 'vue'
import { t } from '@/lang'
import { img } from '@/utils/common'
import type { FormInstance, ElNotification } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { getDiyBottomConfig, setDiyBottomConfig } from '@/app/api/diy'
import Sortable from 'sortablejs'
import { range } from 'lodash-es'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const activeName = ref<string>('navPicture')
const loading = ref<boolean>(false)
route.query.key = route.query.key || ''
// 底部导航数据
const diyBottomData = reactive({
key: '',
info: {},
value: {
backgroundColor: '#FFFFFF',
textColor: '#333333',
textHoverColor: '#333333',
type: '1',
list: [],
},
})
// 底部导航项数据
const diyBottomItemData = reactive({
text: '',
link: {
name: '',
title: '',
parent: '',
url: '',
},
iconSelectPath: '',
iconPath: '',
})
// 添加导航
const addNav = (): void => {
if (diyBottomData.value.list.length >= 5) return
diyBottomData.value.list.push({ ...diyBottomItemData })
}
addNav()
// 删除导航
const deleteNav = (index: any): void => {
const data = diyBottomData.value.list
data.splice(index, 1)
}
const formRef = ref<FormInstance>()
/**
* 获取导航数据
*/
const getDiyBottomFn = () => {
loading.value = true
getDiyBottomConfig({
key: route.query.key,
})
.then((res) => {
loading.value = false
Object.keys(diyBottomData).forEach((item, index) => {
diyBottomData[item] = res.data[item]
})
})
.catch(() => {
loading.value = false
})
}
getDiyBottomFn()
// 保存导航数据
const onSave = async (formEl: FormInstance | undefined) => {
if (verifyFn()) return false
if (loading.value || !formEl) return
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
setDiyBottomConfig({ key: diyBottomData.key, value: diyBottomData.value })
.then((res) => {
loading.value = false
})
.catch(() => {
loading.value = false
})
}
})
}
const back = () => {
router.push('/diy/tabbar')
}
// 验证
const verifyFn = (): boolean => {
if (diyBottomData.value.list.length < 2) {
ElNotification({
type: 'error',
message: t('leastTwoNav'),
})
return true
}
try {
const msg = ref<string>('')
diyBottomData.value.list.forEach((item: any, index) => {
if (!item.iconPath)
msg.value = `${t('pleaseUpload')}${index + 1}${t('navIcon')}`
if (!item.iconSelectPath)
msg.value = `${t('pleaseUpload')}${index + 1}${t('navSelectIcon')}`
if (!item.text)
msg.value = `${t('pleaseEnter')}[${index + 1}${t('navTitle')}`
if (!item.link.url)
msg.value = `${t('pleaseChoose')}${index + 1}${t('navLink')}`
if (msg.value) {
ElNotification({
type: 'error',
message: msg.value,
})
throw Error()
}
})
} catch (e) {
return true
}
return false
}
const navItemRef = ref()
onMounted(() => {
const sortable = Sortable.create(navItemRef.value, {
group: 'item-wrap',
animation: 200,
filter: '.not-sort', // 过滤.not-sort的元素
onEnd: (event) => {
const temp = diyBottomData.value.list[event.oldIndex!]
diyBottomData.value.list.splice(event.oldIndex!, 1)
diyBottomData.value.list.splice(event.newIndex!, 0, temp)
nextTick(() => {
sortable.sort(
range(diyBottomData.value.list.length).map((value) => {
return value.toString()
})
)
})
},
})
})
const useDrag = ref(false)
const diyLinkFn = (val) => {
useDrag.value = val
}
</script>
<style lang="scss" scoped>
.close-icon {
display: none;
position: absolute !important;
font-size: 20px !important;
color: #7d7b7b !important;
}
.list-item:hover .close-icon {
display: block;
}
</style>