修复bug ,底部导航,装修优化
This commit is contained in:
parent
a1fc2fed1c
commit
cf99b35b79
|
|
@ -30,6 +30,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rushstack/eslint-patch": "^1.1.0",
|
"@rushstack/eslint-patch": "^1.1.0",
|
||||||
|
"@tailwindcss/line-clamp": "^0.4.2",
|
||||||
"@types/lodash-es": "^4.17.6",
|
"@types/lodash-es": "^4.17.6",
|
||||||
"@types/node": "^16.11.41",
|
"@types/node": "^16.11.41",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,11 @@ export function setDecoratePages(params: any) {
|
||||||
return request.post({ url: '/decorate/pages/save', params })
|
return request.post({ url: '/decorate/pages/save', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取首页文章数据
|
||||||
|
export function getDecorateArticle(params?: any) {
|
||||||
|
return request.get({ url: '/decorate/data/article', params })
|
||||||
|
}
|
||||||
|
|
||||||
// 底部导航详情
|
// 底部导航详情
|
||||||
export function getDecorateTabbar(params?: any) {
|
export function getDecorateTabbar(params?: any) {
|
||||||
return request.get({ url: '/decorate/tabbar/detail', params })
|
return request.get({ url: '/decorate/tabbar/detail', params })
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<el-image :style="styles" v-bind="props">
|
<el-image :style="styles" v-bind="props"> </el-image>
|
||||||
<template v-slot:error>
|
|
||||||
<div class="text-tx-secondary flex items-center justify-center bg-white h-full">
|
|
||||||
<icon name="el-icon-Picture" :size="30" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-image>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
自定义链接
|
自定义链接
|
||||||
<div class="ml-4 flex-1 min-w-[100px]">
|
<div class="ml-4 flex-1 min-w-[100px]">
|
||||||
<el-input
|
<el-input
|
||||||
:model-value="modelValue.path"
|
:model-value="modelValue.query?.url"
|
||||||
placeholder="请输入链接地址"
|
placeholder="请输入链接地址"
|
||||||
@input="handleInput"
|
@input="handleInput"
|
||||||
/>
|
/>
|
||||||
|
|
@ -32,7 +32,10 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const handleInput = (value: string) => {
|
const handleInput = (value: string) => {
|
||||||
emit('update:modelValue', {
|
emit('update:modelValue', {
|
||||||
path: value,
|
path: '/pages/webview/webview',
|
||||||
|
query: {
|
||||||
|
url: value
|
||||||
|
},
|
||||||
type: LinkTypeEnum.CUSTOM_LINK
|
type: LinkTypeEnum.CUSTOM_LINK
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,8 @@
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
<div class="flex-1 pl-4">
|
<div class="flex-1 pl-4">
|
||||||
<shop-pages
|
<shop-pages v-model="activeLink" v-if="LinkTypeEnum.SHOP_PAGES == activeMenu" />
|
||||||
v-model="activeLink"
|
<custom-link v-model="activeLink" v-if="LinkTypeEnum.CUSTOM_LINK == activeMenu" />
|
||||||
v-if="LinkTypeEnum.SHOP_PAGES == activeMenu"
|
|
||||||
@update:model-value="updateLink"
|
|
||||||
/>
|
|
||||||
<custom-link
|
|
||||||
v-model="activeLink"
|
|
||||||
v-if="LinkTypeEnum.CUSTOM_LINK == activeMenu"
|
|
||||||
@update:model-value="updateLink"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -72,9 +64,10 @@ const handleSelect = (index: string) => {
|
||||||
activeMenu.value = index
|
activeMenu.value = index
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateLink = (value: any) => {
|
watch(activeLink, (value) => {
|
||||||
|
if (!value.type) return
|
||||||
emit('update:modelValue', value)
|
emit('update:modelValue', value)
|
||||||
}
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="link-picker flex-1" @click="!disabled && popupRef?.open()">
|
<div class="link-picker flex-1" @click="!disabled && popupRef?.open()">
|
||||||
<el-input
|
<el-input :model-value="getLink" placeholder="请选择链接" readonly :disabled="disabled">
|
||||||
:model-value="modelValue?.name ?? modelValue?.path"
|
|
||||||
placeholder="请选择链接"
|
|
||||||
readonly
|
|
||||||
:disabled="disabled"
|
|
||||||
>
|
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<icon v-if="!modelValue?.path" name="el-icon-ArrowRight" />
|
<icon v-if="!modelValue?.path" name="el-icon-ArrowRight" />
|
||||||
<icon
|
<icon
|
||||||
|
|
@ -43,6 +38,17 @@ const activeLink = ref<Link>({ path: '', type: LinkTypeEnum.SHOP_PAGES })
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
emit('update:modelValue', activeLink.value)
|
emit('update:modelValue', activeLink.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getLink = computed(() => {
|
||||||
|
switch (props.modelValue?.type) {
|
||||||
|
case LinkTypeEnum.SHOP_PAGES:
|
||||||
|
return props.modelValue.name
|
||||||
|
case LinkTypeEnum.CUSTOM_LINK:
|
||||||
|
return props.modelValue.query?.url
|
||||||
|
default:
|
||||||
|
return props.modelValue?.name
|
||||||
|
}
|
||||||
|
})
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
(value) => {
|
(value) => {
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,6 @@ export function findFirstValidRoute(routes: RouteRecordRaw[]): string | undefine
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRoutePath(perms: string) {
|
export function getRoutePath(perms: string) {
|
||||||
console.log(router.getRoutes())
|
|
||||||
console.log(router.getRoutes().find((item) => item.meta?.perms == perms)?.path)
|
|
||||||
return router.getRoutes().find((item) => item.meta?.perms == perms)?.path || ''
|
return router.getRoutes().find((item) => item.meta?.perms == perms)?.path || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,14 +64,13 @@ export class Axios {
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
(err: AxiosError) => {
|
(err: AxiosError) => {
|
||||||
let cancelUrl = err.config?.url
|
|
||||||
if (isFunction(responseInterceptorsCatchHook)) {
|
if (isFunction(responseInterceptorsCatchHook)) {
|
||||||
responseInterceptorsCatchHook(err)
|
responseInterceptorsCatchHook(err)
|
||||||
}
|
}
|
||||||
if (err.code == AxiosError.ERR_CANCELED) {
|
if (err.code != AxiosError.ERR_CANCELED) {
|
||||||
cancelUrl = err.message
|
this.removeCancelToken(err.config?.url!)
|
||||||
}
|
}
|
||||||
this.removeCancelToken(cancelUrl!)
|
|
||||||
if (err.code == AxiosError.ECONNABORTED) {
|
if (err.code == AxiosError.ECONNABORTED) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
|
|
||||||
|
|
@ -149,3 +149,14 @@ export const timeFormat = (dateTime: number, fmt = 'yyyy-mm-dd') => {
|
||||||
}
|
}
|
||||||
return fmt
|
return fmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取不重复的id
|
||||||
|
* @param length { Number } id的长度
|
||||||
|
* @return { String } id
|
||||||
|
*/
|
||||||
|
export const getNonDuplicateID = (length = 8) => {
|
||||||
|
let idStr = Date.now().toString(36)
|
||||||
|
idStr += Math.random().toString(36).substring(3, length)
|
||||||
|
return idStr
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-card class="!border-none" shadow="never">
|
<el-card class="!border-none" shadow="never">
|
||||||
<el-alert type="warning" title="温馨提示:用于管理网站的分类,只可添加到一级" :closable="false" show-icon />
|
<el-alert
|
||||||
|
type="warning"
|
||||||
|
title="温馨提示:用于管理网站的分类,只可添加到一级"
|
||||||
|
:closable="false"
|
||||||
|
show-icon
|
||||||
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card class="!border-none mt-4" shadow="never" v-loading="pager.loading">
|
<el-card class="!border-none mt-4" shadow="never" v-loading="pager.loading">
|
||||||
<div>
|
<div>
|
||||||
<el-button class="mb-4" v-perms="['article:cate:add']" type="primary" @click="handleAdd()">
|
<el-button
|
||||||
|
class="mb-4"
|
||||||
|
v-perms="['article:cate:add']"
|
||||||
|
type="primary"
|
||||||
|
@click="handleAdd()"
|
||||||
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon name="el-icon-Plus" />
|
<icon name="el-icon-Plus" />
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -17,17 +27,32 @@
|
||||||
<el-table-column label="文章数" prop="number" min-width="120" />
|
<el-table-column label="文章数" prop="number" min-width="120" />
|
||||||
<el-table-column label="状态" min-width="120">
|
<el-table-column label="状态" min-width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-switch v-perms="['article:cate:change']" v-model="row.isShow" :active-value="1"
|
<el-switch
|
||||||
:inactive-value="0" @change="changeStatus(row.id)" />
|
v-perms="['article:cate:change']"
|
||||||
|
v-model="row.isShow"
|
||||||
|
:active-value="1"
|
||||||
|
:inactive-value="0"
|
||||||
|
@change="changeStatus(row.id)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="排序" prop="sort" min-width="120" />
|
<el-table-column label="排序" prop="sort" min-width="120" />
|
||||||
<el-table-column label="操作" width="120" fixed="right">
|
<el-table-column label="操作" width="120" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button v-perms="['article:cate:edit']" type="primary" link @click="handleEdit(row)">
|
<el-button
|
||||||
|
v-perms="['article:cate:edit']"
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="handleEdit(row)"
|
||||||
|
>
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-perms="['article:cate:del']" type="danger" link @click="handleDelete(row.id)">
|
<el-button
|
||||||
|
v-perms="['article:cate:del']"
|
||||||
|
type="danger"
|
||||||
|
link
|
||||||
|
@click="handleDelete(row.id)"
|
||||||
|
>
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@
|
||||||
:src="row.image"
|
:src="row.image"
|
||||||
class="w-[60px] h-[45px]"
|
class="w-[60px] h-[45px]"
|
||||||
:preview-src-list="[row.image]"
|
:preview-src-list="[row.image]"
|
||||||
|
preview-teleported
|
||||||
|
fit="contain"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<el-image :style="styles" v-bind="props" :src="getImageUrl(src)">
|
||||||
|
<template #placeholder>
|
||||||
|
<div class="image-slot"></div>
|
||||||
|
</template>
|
||||||
|
<template #error>
|
||||||
|
<div class="image-slot">
|
||||||
|
<icon name="el-icon-Picture" :size="30" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import type { CSSProperties } from 'vue'
|
||||||
|
import { addUnit } from '@/utils/util'
|
||||||
|
import { imageProps } from 'element-plus'
|
||||||
|
import useAppStore from '@/stores/modules/app'
|
||||||
|
const props = defineProps({
|
||||||
|
width: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 'auto'
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 'auto'
|
||||||
|
},
|
||||||
|
radius: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
...imageProps
|
||||||
|
})
|
||||||
|
|
||||||
|
const { getImageUrl } = useAppStore()
|
||||||
|
const styles = computed<CSSProperties>(() => {
|
||||||
|
return {
|
||||||
|
width: addUnit(props.width),
|
||||||
|
height: addUnit(props.height),
|
||||||
|
borderRadius: addUnit(props.radius)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.el-image {
|
||||||
|
display: block;
|
||||||
|
.image-slot {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #fafafa;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="shadow mx-[30px] pages-preview">
|
<div class="shadow mx-[30px] pages-preview">
|
||||||
<div
|
<el-scrollbar>
|
||||||
v-for="(widget, index) in pageData"
|
|
||||||
:key="widget"
|
|
||||||
class="relative"
|
|
||||||
:class="{
|
|
||||||
'cursor-pointer': !widget?.disabled
|
|
||||||
}"
|
|
||||||
@click="handleClick(widget, index)"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="absolute w-full h-full z-[100] border-dashed"
|
v-for="(widget, index) in pageData"
|
||||||
|
:key="widget.id"
|
||||||
|
class="relative"
|
||||||
:class="{
|
:class="{
|
||||||
select: index == modelValue,
|
'cursor-pointer': !widget?.disabled
|
||||||
'border-br border-2': !widget?.disabled
|
|
||||||
}"
|
}"
|
||||||
></div>
|
@click="handleClick(widget, index)"
|
||||||
<slot>
|
>
|
||||||
<keep-alive>
|
<div
|
||||||
|
class="absolute w-full h-full z-[100] border-dashed"
|
||||||
|
:class="{
|
||||||
|
select: index == modelValue,
|
||||||
|
'border-[#dcdfe6] border-2': !widget?.disabled
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
<slot>
|
||||||
<component
|
<component
|
||||||
:is="widgets[widget?.name]?.content"
|
:is="widgets[widget?.name]?.content"
|
||||||
:content="widget.content"
|
:content="widget.content"
|
||||||
:styles="widget.styles"
|
:styles="widget.styles"
|
||||||
|
:key="widget.id"
|
||||||
/>
|
/>
|
||||||
</keep-alive>
|
</slot>
|
||||||
</slot>
|
</div>
|
||||||
</div>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="banner">
|
<div class="banner">
|
||||||
<div class="banner-image">
|
<div class="banner-image">
|
||||||
<image-contain width="100%" height="170px" :src="getImageUrl(getImage)" fit="contain" />
|
<decoration-img width="100%" height="170px" :src="getImage" fit="contain" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useAppStore from '@/stores/modules/app'
|
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import type options from './options'
|
import type options from './options'
|
||||||
|
import DecorationImg from '../../decoration-img.vue'
|
||||||
type OptionsType = ReturnType<typeof options>
|
type OptionsType = ReturnType<typeof options>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
content: {
|
content: {
|
||||||
|
|
@ -20,7 +20,7 @@ const props = defineProps({
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const { getImageUrl } = useAppStore()
|
|
||||||
const getImage = computed(() => {
|
const getImage = computed(() => {
|
||||||
const { data } = props.content
|
const { data } = props.content
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="customer-service">
|
<div class="customer-service">
|
||||||
<image-contain width="140px" height="140px" :src="getImageUrl(content.qrcode)" alt="" />
|
<decoration-img width="140px" height="140px" :src="content.qrcode" alt="" />
|
||||||
<div class="text-[15px] mt-[7px] font-medium">{{ content.title }}</div>
|
<div class="text-[15px] mt-[7px] font-medium">{{ content.title }}</div>
|
||||||
<div class="text-[#666] mt-[20px]">服务时间:{{ content.time }}</div>
|
<div class="text-[#666] mt-[20px]">服务时间:{{ content.time }}</div>
|
||||||
<div class="text-[#666] mt-[7px]">客服电话:{{ content.mobile }}</div>
|
<div class="text-[#666] mt-[7px]">客服电话:{{ content.mobile }}</div>
|
||||||
|
|
@ -12,9 +12,9 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useAppStore from '@/stores/modules/app'
|
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import type options from './options'
|
import type options from './options'
|
||||||
|
import DecorationImg from '../../decoration-img.vue'
|
||||||
type OptionsType = ReturnType<typeof options>
|
type OptionsType = ReturnType<typeof options>
|
||||||
defineProps({
|
defineProps({
|
||||||
content: {
|
content: {
|
||||||
|
|
@ -26,7 +26,6 @@ defineProps({
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const { getImageUrl } = useAppStore()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
:key="index"
|
:key="index"
|
||||||
class="flex flex-col items-center w-1/4 mb-[15px]"
|
class="flex flex-col items-center w-1/4 mb-[15px]"
|
||||||
>
|
>
|
||||||
<image-contain width="26px" height="26px" :src="getImageUrl(item.image)" alt="" />
|
<decoration-img width="26px" height="26px" :src="item.image" alt="" />
|
||||||
<div class="mt-[7px]">{{ item.name }}</div>
|
<div class="mt-[7px]">{{ item.name }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
:key="index"
|
:key="index"
|
||||||
class="flex items-center border-b border-[#e5e5e5] h-[50px] px-[12px]"
|
class="flex items-center border-b border-[#e5e5e5] h-[50px] px-[12px]"
|
||||||
>
|
>
|
||||||
<image-contain width="24px" height="24px" :src="getImageUrl(item.image)" alt="" />
|
<decoration-img width="24px" height="24px" :src="item.image" alt="" />
|
||||||
<div class="ml-[10px] flex-1">{{ item.name }}</div>
|
<div class="ml-[10px] flex-1">{{ item.name }}</div>
|
||||||
<div>
|
<div>
|
||||||
<icon name="el-icon-ArrowRight" />
|
<icon name="el-icon-ArrowRight" />
|
||||||
|
|
@ -29,9 +29,9 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useAppStore from '@/stores/modules/app'
|
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import type options from './options'
|
import type options from './options'
|
||||||
|
import DecorationImg from '../../decoration-img.vue'
|
||||||
type OptionsType = ReturnType<typeof options>
|
type OptionsType = ReturnType<typeof options>
|
||||||
defineProps({
|
defineProps({
|
||||||
content: {
|
content: {
|
||||||
|
|
@ -43,7 +43,6 @@ defineProps({
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const { getImageUrl } = useAppStore()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="nav pt-[15px] pb-[8px]">
|
<div class="nav bg-white pt-[15px] pb-[8px]">
|
||||||
<div class="flex flex-wrap">
|
<div class="flex flex-wrap">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in content.data"
|
v-for="(item, index) in content.data"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="flex flex-col items-center w-1/5 mb-[15px]"
|
class="flex flex-col items-center w-1/5 mb-[15px]"
|
||||||
>
|
>
|
||||||
<image-contain width="41px" height="41px" :src="getImageUrl(item.image)" alt="" />
|
<decoration-img width="41px" height="41px" :src="item.image" alt="" />
|
||||||
<div class="mt-[7px]">{{ item.name }}</div>
|
<div class="mt-[7px]">{{ item.name }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useAppStore from '@/stores/modules/app'
|
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import type options from './options'
|
import type options from './options'
|
||||||
|
import DecorationImg from '../../decoration-img.vue'
|
||||||
type OptionsType = ReturnType<typeof options>
|
type OptionsType = ReturnType<typeof options>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
content: {
|
content: {
|
||||||
|
|
@ -27,11 +27,6 @@ const props = defineProps({
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const { getImageUrl } = useAppStore()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped></style>
|
||||||
.nav {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
import type options from './options'
|
||||||
|
type OptionsType = ReturnType<typeof options>
|
||||||
|
defineProps({
|
||||||
|
content: {
|
||||||
|
type: Object as PropType<OptionsType['content']>,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
type: Object as PropType<OptionsType['styles']>,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<div class="news">
|
||||||
|
<div class="flex items-center news-title mx-[10px] my-[15px] text-[17px] font-medium">
|
||||||
|
最新资讯
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="item in newsList"
|
||||||
|
:key="item.id"
|
||||||
|
class="news-card flex bg-white px-[10px] py-[16px] text-[#333] border-[#f2f2f2] border-b"
|
||||||
|
>
|
||||||
|
<div class="mr-[10px]" v-if="item.image">
|
||||||
|
<img :src="item.image" class="w-[120px] h-[90px]" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col justify-between flex-1">
|
||||||
|
<div class="text-[15px] font-medium line-clamp-2">{{ item.title }}</div>
|
||||||
|
<div class="line-clamp-1 text-sm mt-[8px]">
|
||||||
|
{{ item.intro }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-[#999] text-xs w-full flex justify-between mt-[8px]">
|
||||||
|
<div>{{ item.createTime }}</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<icon name="el-icon-View" />
|
||||||
|
<div class="ml-[5px]">{{ item.visit }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { PropType } from 'vue'
|
||||||
|
import { getDecorateArticle } from '@/api/decoration'
|
||||||
|
import type options from './options'
|
||||||
|
type OptionsType = ReturnType<typeof options>
|
||||||
|
const props = defineProps({
|
||||||
|
content: {
|
||||||
|
type: Object as PropType<OptionsType['content']>,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
type: Object as PropType<OptionsType['styles']>,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const newsList = ref<any[]>([])
|
||||||
|
const getData = async () => {
|
||||||
|
const data = await getDecorateArticle({
|
||||||
|
limit: 10
|
||||||
|
})
|
||||||
|
newsList.value = data
|
||||||
|
}
|
||||||
|
getData()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.news {
|
||||||
|
.news-title {
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
width: 4px;
|
||||||
|
height: 17px;
|
||||||
|
display: block;
|
||||||
|
margin-right: 5px;
|
||||||
|
background: #4173ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import attr from './attr.vue'
|
||||||
|
import content from './content.vue'
|
||||||
|
import options from './options'
|
||||||
|
export default {
|
||||||
|
attr,
|
||||||
|
content,
|
||||||
|
options
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
export default () => ({
|
||||||
|
title: '资讯',
|
||||||
|
name: 'news',
|
||||||
|
disabled: 1,
|
||||||
|
content: {},
|
||||||
|
styles: {}
|
||||||
|
})
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="banner mx-[10px] mt-[10px]">
|
<div class="banner mx-[10px] mt-[10px]">
|
||||||
<div class="banner-image">
|
<div class="banner-image">
|
||||||
<image-contain width="100%" height="100px" :src="getImageUrl(getImage)" fit="contain" />
|
<decoration-img width="100%" height="100px" :src="getImage" fit="contain" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import useAppStore from '@/stores/modules/app'
|
|
||||||
import type { PropType } from 'vue'
|
import type { PropType } from 'vue'
|
||||||
import type options from './options'
|
import type options from './options'
|
||||||
|
import DecorationImg from '../../decoration-img.vue'
|
||||||
type OptionsType = ReturnType<typeof options>
|
type OptionsType = ReturnType<typeof options>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
content: {
|
content: {
|
||||||
|
|
@ -20,7 +20,6 @@ const props = defineProps({
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const { getImageUrl } = useAppStore()
|
|
||||||
const getImage = computed(() => {
|
const getImage = computed(() => {
|
||||||
const { data } = props.content
|
const { data } = props.content
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="decoration-pages min-w-[1100px]">
|
<div class="decoration-pages min-w-[1100px]">
|
||||||
<el-card shadow="never" class="!border-none flex-1" :body-style="{ height: '100%' }">
|
<el-card shadow="never" class="!border-none flex-1 flex" :body-style="{ flex: 1 }">
|
||||||
<div class="flex h-full items-start">
|
<div class="flex h-full items-start">
|
||||||
<Menu v-model="activeMenu" :menus="menus" />
|
<Menu v-model="activeMenu" :menus="menus" />
|
||||||
<preview v-model="selectWidgetIndex" :pageData="getPageData" />
|
<preview v-model="selectWidgetIndex" :pageData="getPageData" />
|
||||||
|
|
@ -19,6 +19,7 @@ import AttrSetting from '../component/pages/attr-setting.vue'
|
||||||
import widgets from '../component/widgets'
|
import widgets from '../component/widgets'
|
||||||
import { getDecoratePages, setDecoratePages } from '@/api/decoration'
|
import { getDecoratePages, setDecoratePages } from '@/api/decoration'
|
||||||
import feedback from '@/utils/feedback'
|
import feedback from '@/utils/feedback'
|
||||||
|
import { getNonDuplicateID } from '@/utils/util'
|
||||||
enum pagesTypeEnum {
|
enum pagesTypeEnum {
|
||||||
HOME = '1',
|
HOME = '1',
|
||||||
USER = '2',
|
USER = '2',
|
||||||
|
|
@ -26,7 +27,13 @@ enum pagesTypeEnum {
|
||||||
}
|
}
|
||||||
|
|
||||||
const generatePageData = (widgetNames: string[]) => {
|
const generatePageData = (widgetNames: string[]) => {
|
||||||
return widgetNames.map((widgetName) => widgets[widgetName]?.options() || {})
|
return widgetNames.map((widgetName) => {
|
||||||
|
const options = {
|
||||||
|
id: getNonDuplicateID(),
|
||||||
|
...(widgets[widgetName]?.options() || {})
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const menus: Record<
|
const menus: Record<
|
||||||
|
|
@ -41,7 +48,7 @@ const menus: Record<
|
||||||
id: 1,
|
id: 1,
|
||||||
pageType: 1,
|
pageType: 1,
|
||||||
name: '商城首页',
|
name: '商城首页',
|
||||||
pageData: generatePageData(['search', 'banner', 'nav'])
|
pageData: generatePageData(['search', 'banner', 'nav', 'news'])
|
||||||
},
|
},
|
||||||
[pagesTypeEnum.USER]: {
|
[pagesTypeEnum.USER]: {
|
||||||
id: 2,
|
id: 2,
|
||||||
|
|
@ -70,6 +77,7 @@ const getData = async () => {
|
||||||
const data = await getDecoratePages({ id: activeMenu.value })
|
const data = await getDecoratePages({ id: activeMenu.value })
|
||||||
menus[String(data.id)].pageData = JSON.parse(data.pageData)
|
menus[String(data.id)].pageData = JSON.parse(data.pageData)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setData = async () => {
|
const setData = async () => {
|
||||||
await setDecoratePages({
|
await setDecoratePages({
|
||||||
...menus[activeMenu.value],
|
...menus[activeMenu.value],
|
||||||
|
|
|
||||||
|
|
@ -60,5 +60,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: []
|
plugins: [
|
||||||
|
require('@tailwindcss/line-clamp') // 引入插件
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -426,6 +426,11 @@
|
||||||
resolved "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz#0c8b74c50f29ee44f423f7416829c0bf8bb5eb27"
|
resolved "https://registry.npmmirror.com/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz#0c8b74c50f29ee44f423f7416829c0bf8bb5eb27"
|
||||||
integrity sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==
|
integrity sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==
|
||||||
|
|
||||||
|
"@tailwindcss/line-clamp@^0.4.2":
|
||||||
|
version "0.4.2"
|
||||||
|
resolved "https://registry.npmmirror.com/@tailwindcss/line-clamp/-/line-clamp-0.4.2.tgz#f353c5a8ab2c939c6267ac5b907f012e5ee130f9"
|
||||||
|
integrity sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==
|
||||||
|
|
||||||
"@transloadit/prettier-bytes@0.0.7":
|
"@transloadit/prettier-bytes@0.0.7":
|
||||||
version "0.0.7"
|
version "0.0.7"
|
||||||
resolved "https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz#cdb5399f445fdd606ed833872fa0cabdbc51686b"
|
resolved "https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz#cdb5399f445fdd606ed833872fa0cabdbc51686b"
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<u-tabbar v-bind="tabbarStyle" :list="tabbarList" @change="handleChange"></u-tabbar>
|
<u-tabbar
|
||||||
|
v-model="current"
|
||||||
|
v-bind="tabbarStyle"
|
||||||
|
:list="tabbarList"
|
||||||
|
@change="handleChange"
|
||||||
|
:hide-tab-bar="false"
|
||||||
|
></u-tabbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useAppStore } from '@/stores/app'
|
import { useAppStore } from '@/stores/app'
|
||||||
import { currentPage, navigateTo } from '@/utils/util'
|
import { navigateTo } from '@/utils/util'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { computed, ref } from 'vue'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
const current = ref()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const tabbarList = computed(() => {
|
const tabbarList = computed(() => {
|
||||||
return appStore.getTabbarConfig.map((item: any) => ({
|
return appStore.getTabbarConfig.map((item: any) => {
|
||||||
iconPath: item.unselected,
|
const link = JSON.parse(item.link)
|
||||||
selectedIconPath: item.selected,
|
return {
|
||||||
text: item.name,
|
iconPath: item.unselected,
|
||||||
link: JSON.parse(item.link),
|
selectedIconPath: item.selected,
|
||||||
pagePath: JSON.parse(item.link).path
|
text: item.name,
|
||||||
}))
|
link,
|
||||||
|
pagePath: link.path
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const tabbarStyle = computed(() => ({
|
const tabbarStyle = computed(() => ({
|
||||||
|
|
@ -26,8 +35,4 @@ const handleChange = (index: number) => {
|
||||||
const selectTab = tabbarList.value[index]
|
const selectTab = tabbarList.value[index]
|
||||||
navigateTo(selectTab.link, 'reLaunch')
|
navigateTo(selectTab.link, 'reLaunch')
|
||||||
}
|
}
|
||||||
// onMounted(() => {
|
|
||||||
// const page = currentPage()
|
|
||||||
// console.log(page)
|
|
||||||
// })
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -238,16 +238,20 @@ export default {
|
||||||
},
|
},
|
||||||
// 切换tab
|
// 切换tab
|
||||||
switchTab(index) {
|
switchTab(index) {
|
||||||
// 发出事件和修改v-model绑定的值
|
|
||||||
this.$emit("change", index);
|
let pagePath = this.list[index].pagePath;
|
||||||
// 如果有配置pagePath属性,使用uni.switchTab进行跳转
|
// 如果有配置pagePath属性,使用uni.switchTab进行跳转
|
||||||
if (this.list[index].pagePath) {
|
if (pagePath) {
|
||||||
|
if(pagePath == this.pageUrl || pagePath == "/" + this.pageUrl) return
|
||||||
|
// 发出事件和修改v-model绑定的值
|
||||||
|
this.$emit("change", index);
|
||||||
uni.switchTab({
|
uni.switchTab({
|
||||||
url: this.list[index].pagePath
|
url: pagePath
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 如果配置了papgePath属性,将不会双向绑定v-model传入的value值
|
// 如果配置了papgePath属性,将不会双向绑定v-model传入的value值
|
||||||
// 因为这个模式下,不再需要v-model绑定的value值了,而是通过getCurrentPages()适配
|
// 因为这个模式下,不再需要v-model绑定的value值了,而是通过getCurrentPages()适配
|
||||||
|
this.$emit("change", index);
|
||||||
this.$emit("input", index);
|
this.$emit("input", index);
|
||||||
this.$emit("update:modelValue", index);
|
this.$emit("update:modelValue", index);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,15 +53,8 @@ export enum LinkTypeEnum {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function navigateTo(link: Link, navigateType: 'navigateTo' | 'reLaunch' = 'navigateTo') {
|
export function navigateTo(link: Link, navigateType: 'navigateTo' | 'reLaunch' = 'navigateTo') {
|
||||||
let url: string
|
const url = link.query ? `${link.path}?${objectToQuery(link.query)}` : link.path
|
||||||
switch (link.type) {
|
uni[navigateType]({ url })
|
||||||
case LinkTypeEnum.SHOP_PAGES:
|
|
||||||
url = link.query ? `${link.path}?${objectToQuery(link.query)}` : link.path
|
|
||||||
uni[navigateType]({ url })
|
|
||||||
break
|
|
||||||
case LinkTypeEnum.CUSTOM_LINK:
|
|
||||||
uni[navigateType]({ url: `/pages/webview/webview?url=${link.path}` })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue