优化网站导航模块
This commit is contained in:
@@ -87,6 +87,29 @@
|
||||
/>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item label="所在页面">
|
||||
<SelectDesign
|
||||
:placeholder="`请选择页面`"
|
||||
v-model:value="form.pageName"
|
||||
@done="choosePageId"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="宽度">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="3000"
|
||||
placeholder="px"
|
||||
v-model:value="form.width"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="高度">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="2000"
|
||||
placeholder="px"
|
||||
v-model:value="form.height"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="标题" name="name">
|
||||
<a-input
|
||||
allow-clear
|
||||
@@ -125,19 +148,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject, isChinese } from 'ele-admin-pro';
|
||||
import { assignObject } from 'ele-admin-pro';
|
||||
import { addAd, updateAd } from '@/api/cms/ad';
|
||||
import { Ad } from '@/api/cms/ad/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { FormInstance, type Rule, RuleObject } from 'ant-design-vue/es/form';
|
||||
import { FormInstance, type Rule } from 'ant-design-vue/es/form';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { uploadFile } from '@/api/system/file';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
import { checkExistence } from '@/api/system/company';
|
||||
import image from '@/views/cms/photo/image.vue';
|
||||
import defaultResult from 'ant-design-vue/es/_util/isMobile';
|
||||
import any = defaultResult.any;
|
||||
import { Design } from '@/api/cms/design/model';
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
@@ -180,7 +200,9 @@
|
||||
type: '',
|
||||
status: 0,
|
||||
comments: '',
|
||||
sortNumber: 100
|
||||
sortNumber: 100,
|
||||
pageName: '',
|
||||
pageId: undefined
|
||||
});
|
||||
|
||||
/* 更新visible */
|
||||
@@ -274,6 +296,11 @@
|
||||
form.images = '';
|
||||
};
|
||||
|
||||
const choosePageId = (data: Design) => {
|
||||
form.pageName = data.name;
|
||||
form.pageId = data.pageId;
|
||||
};
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
@@ -323,15 +350,14 @@
|
||||
});
|
||||
});
|
||||
}
|
||||
if (props.data.adType == '幻灯片') {
|
||||
const arr = JSON.parse(props.data.path);
|
||||
arr.map((d) => {
|
||||
pathList.value.push(d);
|
||||
});
|
||||
}
|
||||
// if (props.data.adType == '幻灯片') {
|
||||
// const arr = JSON.parse(props.data.path);
|
||||
// arr.map((d) => {
|
||||
// pathList.value.push(d);
|
||||
// });
|
||||
// }
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
images.value = [];
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -27,14 +27,9 @@
|
||||
<AntDesignOutlined />
|
||||
</template>
|
||||
</a-avatar>
|
||||
<a
|
||||
@click="
|
||||
openPreview(
|
||||
`https://${record.tenantId}.wsdns.cn/a/${record.articleId}`
|
||||
)
|
||||
"
|
||||
>{{ record.title }}</a
|
||||
>
|
||||
<a @click="openPreview(`/article/detail/${record.articleId}`)">{{
|
||||
record.title
|
||||
}}</a>
|
||||
</a-space>
|
||||
</template>
|
||||
<template v-if="column.key === 'image'"> </template>
|
||||
|
||||
@@ -66,15 +66,9 @@
|
||||
style="margin-right: 10px"
|
||||
v-if="record.image"
|
||||
/>
|
||||
<a
|
||||
@click="
|
||||
openPreview(
|
||||
`https://${record.tenantId}.wsdns.cn/article/` +
|
||||
record.categoryId
|
||||
)
|
||||
"
|
||||
>{{ record.title }}</a
|
||||
>
|
||||
<a @click="openPreview(`/article/` + record.categoryId)">{{
|
||||
record.title
|
||||
}}</a>
|
||||
</template>
|
||||
<template v-if="column.key === 'showIndex'">
|
||||
<a-space @click="onShowIndex(record)">
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
<!-- 最新动态 -->
|
||||
<template>
|
||||
<a-card :title="title" :bordered="false" :body-style="{ padding: '6px 0' }">
|
||||
<template #extra>
|
||||
<more-icon @remove="onRemove" @edit="onEdit" />
|
||||
</template>
|
||||
<div
|
||||
style="height: 346px; padding: 22px 20px 0 20px"
|
||||
class="ele-scrollbar-hover"
|
||||
>
|
||||
<a-timeline>
|
||||
<a-timeline-item
|
||||
v-for="item in activities"
|
||||
:key="item.id"
|
||||
:color="item.color"
|
||||
>
|
||||
<em>{{ item.time }}</em>
|
||||
<em>{{ item.title }}</em>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import MoreIcon from './more-icon.vue';
|
||||
|
||||
defineProps<{
|
||||
title?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'remove'): void;
|
||||
(e: 'edit'): void;
|
||||
}>();
|
||||
|
||||
interface Activitie {
|
||||
id: number;
|
||||
title: string;
|
||||
time: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
// 最新动态数据
|
||||
const activities = ref<Activitie[]>([]);
|
||||
|
||||
/* 查询最新动态 */
|
||||
const queryActivities = () => {
|
||||
activities.value = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'SunSmile 解决了bug 登录提示操作失败',
|
||||
time: '20:30',
|
||||
color: 'gray'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Jasmine 解决了bug 按钮颜色与设计不符',
|
||||
time: '19:30',
|
||||
color: 'gray'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '项目经理 指派了任务 解决项目一的bug',
|
||||
time: '18:30'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '项目经理 指派了任务 解决项目二的bug',
|
||||
time: '17:30'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '项目经理 指派了任务 解决项目三的bug',
|
||||
time: '16:30'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: '项目经理 指派了任务 解决项目四的bug',
|
||||
time: '15:30',
|
||||
color: 'gray'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: '项目经理 指派了任务 解决项目五的bug',
|
||||
time: '14:30',
|
||||
color: 'gray'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: '项目经理 指派了任务 解决项目六的bug',
|
||||
time: '12:30',
|
||||
color: 'gray'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: '项目经理 指派了任务 解决项目七的bug',
|
||||
time: '11:30'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: '项目经理 指派了任务 解决项目八的bug',
|
||||
time: '10:30',
|
||||
color: 'gray'
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
title: '项目经理 指派了任务 解决项目九的bug',
|
||||
time: '09:30',
|
||||
color: 'green'
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
title: '项目经理 指派了任务 解决项目十的bug',
|
||||
time: '08:30',
|
||||
color: 'red'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
const onRemove = () => {
|
||||
emit('remove');
|
||||
};
|
||||
|
||||
const onEdit = () => {
|
||||
emit('edit');
|
||||
};
|
||||
|
||||
queryActivities();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ele-scrollbar-hover
|
||||
:deep(.ant-timeline-item-last > .ant-timeline-item-content) {
|
||||
min-height: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<a-card :title="title" :bordered="false" :body-style="{ padding: '2px' }">
|
||||
<template #extra
|
||||
><a @click="openUrl('/oa/app/index')" class="ele-text-placeholder"
|
||||
>更多<RightOutlined /></a
|
||||
></template>
|
||||
<a-list :size="`small`" :split="false" :data-source="list">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<div class="app-box">
|
||||
<a-image
|
||||
:height="45"
|
||||
:width="45"
|
||||
:preview="false"
|
||||
:src="item.appIcon"
|
||||
fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"
|
||||
/>
|
||||
<div class="app-info">
|
||||
<a
|
||||
class="ele-text-heading"
|
||||
>
|
||||
{{ item.appName }}
|
||||
</a>
|
||||
<span class="ele-text-placeholder">
|
||||
{{ item.appCode }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { Article } from '@/api/cms/article/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { pageApp } from '@/api/oa/app';
|
||||
import { RightOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
title: string;
|
||||
}>();
|
||||
|
||||
const list = ref<Article[]>([]);
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
const reload = () => {
|
||||
const { title } = props;
|
||||
// 加载文章列表
|
||||
pageApp({
|
||||
limit: 5,
|
||||
status: 0,
|
||||
appStatus: '开发中'
|
||||
}).then((data) => {
|
||||
if (data?.list) {
|
||||
list.value = data.list;
|
||||
}
|
||||
});
|
||||
};
|
||||
reload();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DashboardArticleList'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.app-box {
|
||||
display: flex;
|
||||
.app-info {
|
||||
display: flex;
|
||||
margin-left: 5px;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,56 +0,0 @@
|
||||
<template>
|
||||
<a-card :title="title" :bordered="false" :body-style="{ padding: '2px', minHeight: '252px' }">
|
||||
<template #extra
|
||||
><a
|
||||
@click="openPreview('/article/' + categoryId)"
|
||||
class="ele-text-placeholder"
|
||||
>更多<RightOutlined /></a
|
||||
></template>
|
||||
<a-list :size="`small`" :split="false" :data-source="list">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a
|
||||
class="ele-text-secondary"
|
||||
@click="openUrl('/cms/article/' + item.articleId)"
|
||||
>
|
||||
{{ item.title }}
|
||||
</a>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { pageArticle } from '@/api/cms/article';
|
||||
import { Article } from '@/api/cms/article/model';
|
||||
import {openNew, openPreview, openUrl} from '@/utils/common';
|
||||
import { RightOutlined } from '@ant-design/icons-vue';
|
||||
const list = ref<Article[]>([]);
|
||||
|
||||
const props = defineProps<{
|
||||
title: string;
|
||||
categoryId: number;
|
||||
}>();
|
||||
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
const reload = () => {
|
||||
const { categoryId } = props;
|
||||
// 加载文章列表
|
||||
pageArticle({ categoryId, limit: 6 }).then((data) => {
|
||||
if (data?.list) {
|
||||
list.value = data.list;
|
||||
}
|
||||
});
|
||||
};
|
||||
reload();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DashboardArticleList'
|
||||
};
|
||||
</script>
|
||||
@@ -1,80 +0,0 @@
|
||||
<template>
|
||||
<a-card :bordered="false" title="小组成员">
|
||||
<template #extra>
|
||||
<a-tooltip>
|
||||
<template #title>邀请加入</template>
|
||||
<UserAddOutlined @click="onShowQrcode" :style="{ fontSize: '18px' }" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-list
|
||||
class="demo-loadmore-list"
|
||||
item-layout="horizontal"
|
||||
:data-source="list"
|
||||
>
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<template #actions>
|
||||
<a-popover>
|
||||
<template #content> 待处理 </template>
|
||||
<a class="ele-text-danger">{{ item.pending }}</a>
|
||||
</a-popover>
|
||||
<a-popover>
|
||||
<template #content> 本月已处理 </template>
|
||||
<a class="ele-text-secondary">{{ item.month }}</a>
|
||||
</a-popover>
|
||||
</template>
|
||||
<a-skeleton avatar :title="false" :loading="!!item.loading" active>
|
||||
<a-list-item-meta>
|
||||
<template #title>
|
||||
{{ item.nickname }}
|
||||
</template>
|
||||
<template #avatar>
|
||||
<a-avatar :src="item.avatar" />
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-skeleton>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
<!-- 工单二维码 -->
|
||||
<Qrcode v-model:visible="showQrcode" />
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { pageTaskCount } from '@/api/oa/task-count';
|
||||
import { TaskCount } from '@/api/oa/task-count/model';
|
||||
import { UserAddOutlined } from '@ant-design/icons-vue';
|
||||
import Qrcode from './qrcode.vue';
|
||||
|
||||
const showQrcode = ref(false);
|
||||
const list = ref<TaskCount[]>([]);
|
||||
|
||||
pageTaskCount({ limit: 10, roleCode: 'commander' }).then((res) => {
|
||||
if (res) {
|
||||
list.value = res?.list;
|
||||
}
|
||||
});
|
||||
|
||||
const onShowQrcode = () => {
|
||||
showQrcode.value = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.monitor-evaluate-text {
|
||||
width: 90px;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
opacity: 0.8;
|
||||
|
||||
& > .anticon {
|
||||
font-size: 12px;
|
||||
margin: 0 6px 0 8px;
|
||||
}
|
||||
}
|
||||
/deep/.ant-list-item {
|
||||
padding: 7px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,70 +0,0 @@
|
||||
<!-- 本月目标 -->
|
||||
<template>
|
||||
<a-card :title="title" :bordered="false">
|
||||
<template #extra>
|
||||
<more-icon @remove="onRemove" @edit="onEdit" />
|
||||
</template>
|
||||
<div class="workplace-goal-group">
|
||||
<a-progress
|
||||
:width="180"
|
||||
:percent="80"
|
||||
type="dashboard"
|
||||
:stroke-width="4"
|
||||
:show-info="false"
|
||||
/>
|
||||
<div class="workplace-goal-content">
|
||||
<ele-tag color="blue" size="large" shape="circle">
|
||||
<trophy-outlined />
|
||||
</ele-tag>
|
||||
<div class="workplace-goal-num">285</div>
|
||||
</div>
|
||||
<div class="workplace-goal-text">恭喜, 本月目标已达标!</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { TrophyOutlined } from '@ant-design/icons-vue';
|
||||
import MoreIcon from './more-icon.vue';
|
||||
|
||||
defineProps<{
|
||||
title?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'remove'): void;
|
||||
(e: 'edit'): void;
|
||||
}>();
|
||||
|
||||
const onRemove = () => {
|
||||
emit('remove');
|
||||
};
|
||||
|
||||
const onEdit = () => {
|
||||
emit('edit');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workplace-goal-group {
|
||||
height: 310px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
.workplace-goal-content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 180px;
|
||||
margin: -50px 0 0 -90px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.workplace-goal-num {
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,70 +0,0 @@
|
||||
<!-- 本月目标 -->
|
||||
<template>
|
||||
<a-card :title="title" :bordered="false">
|
||||
<template #extra>
|
||||
<more-icon @remove="onRemove" @edit="onEdit" />
|
||||
</template>
|
||||
<div class="workplace-goal-group">
|
||||
<a-progress
|
||||
:width="180"
|
||||
:percent="80"
|
||||
type="dashboard"
|
||||
:stroke-width="4"
|
||||
:show-info="false"
|
||||
/>
|
||||
<div class="workplace-goal-content">
|
||||
<ele-tag color="blue" size="large" shape="circle">
|
||||
<trophy-outlined />
|
||||
</ele-tag>
|
||||
<div class="workplace-goal-num">285</div>
|
||||
</div>
|
||||
<div class="workplace-goal-text">恭喜, 本月目标已达标!</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { TrophyOutlined } from '@ant-design/icons-vue';
|
||||
import MoreIcon from './more-icon.vue';
|
||||
|
||||
defineProps<{
|
||||
title?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'remove'): void;
|
||||
(e: 'edit'): void;
|
||||
}>();
|
||||
|
||||
const onRemove = () => {
|
||||
emit('remove');
|
||||
};
|
||||
|
||||
const onEdit = () => {
|
||||
emit('edit');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workplace-goal-group {
|
||||
height: 310px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
.workplace-goal-content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 180px;
|
||||
margin: -50px 0 0 -90px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.workplace-goal-num {
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,178 +0,0 @@
|
||||
<!-- 快捷方式 -->
|
||||
<template>
|
||||
<a-row :gutter="16" ref="wrapRef">
|
||||
<a-col v-for="item in data" :key="item.url" :lg="3" :md="6" :sm="9" :xs="8">
|
||||
<a-card :bordered="false" hoverable :body-style="{ padding: 0 }">
|
||||
<div class="app-link-block" @click="navTo(item)">
|
||||
<component
|
||||
:is="item.icon"
|
||||
class="app-link-icon"
|
||||
:style="{ color: item.color }"
|
||||
/>
|
||||
<div class="app-link-title">{{ item.title }}</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import SortableJs from 'sortablejs';
|
||||
import type { Row as ARow } from 'ant-design-vue';
|
||||
import { openNew, openUrl } from '@/utils/common';
|
||||
import { getOriginDomain } from '@/utils/domain';
|
||||
const CACHE_KEY = 'workplace-links';
|
||||
// 当前开发环境
|
||||
const env = import.meta.env.MODE;
|
||||
|
||||
interface LinkItem {
|
||||
icon: string;
|
||||
title: string;
|
||||
url: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
// 默认顺序
|
||||
const DEFAULT: LinkItem[] = [
|
||||
{
|
||||
icon: 'settingOutlined',
|
||||
title: '网站设置',
|
||||
url: '/website/setting'
|
||||
},
|
||||
{
|
||||
icon: 'LayoutOutlined',
|
||||
title: '单页管理',
|
||||
url: '/cms/design'
|
||||
},
|
||||
{
|
||||
icon: 'FileSearchOutlined',
|
||||
title: '文章管理',
|
||||
url: '/cms/article'
|
||||
},
|
||||
{
|
||||
icon: 'AntDesignOutlined',
|
||||
title: '广告管理',
|
||||
url: '/website/ad'
|
||||
},
|
||||
{
|
||||
icon: 'InstagramOutlined',
|
||||
title: '图片素材',
|
||||
url: '/cms/photo'
|
||||
},
|
||||
{
|
||||
icon: 'ShoppingOutlined',
|
||||
title: '表单数据',
|
||||
url: '/cms/form-record'
|
||||
},
|
||||
{
|
||||
icon: 'ChromeOutlined',
|
||||
title: '友情链接',
|
||||
url: '/oa/link'
|
||||
},
|
||||
{
|
||||
icon: 'DesktopOutlined',
|
||||
title: '网站首页',
|
||||
url: `${localStorage.getItem('TenantId')}.${localStorage.getItem(
|
||||
'domain'
|
||||
)}`
|
||||
}
|
||||
];
|
||||
|
||||
// 获取缓存的顺序
|
||||
const cache = (() => {
|
||||
const str = localStorage.getItem(CACHE_KEY);
|
||||
try {
|
||||
return str ? JSON.parse(str) : null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
const data = ref<LinkItem[]>([...(cache ?? DEFAULT)]);
|
||||
|
||||
const wrapRef = ref<InstanceType<typeof ARow> | null>(null);
|
||||
|
||||
let sortableIns: SortableJs | null = null;
|
||||
|
||||
/* 重置布局 */
|
||||
const reset = () => {
|
||||
data.value = [...DEFAULT];
|
||||
cacheData();
|
||||
};
|
||||
|
||||
/* 缓存布局 */
|
||||
const cacheData = () => {
|
||||
localStorage.setItem(CACHE_KEY, JSON.stringify(data.value));
|
||||
};
|
||||
|
||||
const navTo = (item) => {
|
||||
if (item.icon == 'DesktopOutlined') {
|
||||
// if (env == 'development') {
|
||||
// return openUrl(getOriginDomain());
|
||||
// }
|
||||
return openNew(`${domain.value}`);
|
||||
}
|
||||
openUrl(item.url);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const isTouchDevice = 'ontouchstart' in document.documentElement;
|
||||
if (isTouchDevice) {
|
||||
return;
|
||||
}
|
||||
sortableIns = new SortableJs(wrapRef.value?.$el, {
|
||||
animation: 300,
|
||||
onUpdate: ({ oldIndex, newIndex }) => {
|
||||
if (typeof oldIndex === 'number' && typeof newIndex === 'number') {
|
||||
const temp = [...data.value];
|
||||
temp.splice(newIndex, 0, temp.splice(oldIndex, 1)[0]);
|
||||
data.value = temp;
|
||||
cacheData();
|
||||
}
|
||||
},
|
||||
setData: () => {}
|
||||
});
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (sortableIns) {
|
||||
sortableIns.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({ reset });
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as icons from './link-icons';
|
||||
import { getSiteInfo } from '@/api/layout';
|
||||
import { ref } from 'vue';
|
||||
|
||||
const tenantId = ref<number>();
|
||||
const domain = ref<string>();
|
||||
|
||||
getSiteInfo().then((data) => {
|
||||
tenantId.value = data.tenantId;
|
||||
domain.value = data.domain;
|
||||
});
|
||||
|
||||
export default {
|
||||
components: icons
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.app-link-block {
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
color: inherit;
|
||||
|
||||
.app-link-icon {
|
||||
color: #666666;
|
||||
font-size: 30px;
|
||||
margin: 6px 0 10px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,14 +0,0 @@
|
||||
export {
|
||||
UserOutlined,
|
||||
TeamOutlined,
|
||||
FileSearchOutlined,
|
||||
ChromeOutlined,
|
||||
ShoppingOutlined,
|
||||
LaptopOutlined,
|
||||
AppstoreAddOutlined,
|
||||
DesktopOutlined,
|
||||
AntDesignOutlined,
|
||||
SettingOutlined,
|
||||
InstagramOutlined,
|
||||
LayoutOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
@@ -1,54 +0,0 @@
|
||||
<template>
|
||||
<a-card :title="linkType" :bordered="false" :body-style="{ padding: '2px' }">
|
||||
<template #extra
|
||||
><a @click="openNew('/oa/link')" class="ele-text-placeholder"
|
||||
>更多<RightOutlined /></a
|
||||
></template>
|
||||
<a-list :size="`small`" :split="false" :data-source="list">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta>
|
||||
<template #avatar>
|
||||
<a-avatar :src="item.icon" />
|
||||
</template>
|
||||
<template #title>
|
||||
<a @click="openUrl(item.url)">{{ item.name }}</a>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { openNew, openUrl } from '@/utils/common';
|
||||
import { pageLink } from '@/api/oa/link';
|
||||
import { RightOutlined } from '@ant-design/icons-vue';
|
||||
const list = ref<any[]>([]);
|
||||
|
||||
const props = defineProps<{
|
||||
linkType: string;
|
||||
}>();
|
||||
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
const reload = () => {
|
||||
const { linkType } = props;
|
||||
// 加载文章列表
|
||||
pageLink({ linkType, limit: 5 }).then((data) => {
|
||||
if (data?.list) {
|
||||
list.value = data.list;
|
||||
}
|
||||
});
|
||||
};
|
||||
reload();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DashboardArticleList'
|
||||
};
|
||||
</script>
|
||||
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<a-dropdown placement="bottomRight">
|
||||
<more-outlined class="ele-text-secondary" style="font-size: 18px" />
|
||||
<template #overlay>
|
||||
<a-menu :selectable="false" @click="onClick">
|
||||
<a-menu-item key="edit">
|
||||
<div class="ele-cell">
|
||||
<edit-outlined />
|
||||
<div class="ele-cell-content">编辑</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="remove">
|
||||
<div class="ele-cell ele-text-danger">
|
||||
<delete-outlined />
|
||||
<div class="ele-cell-content">删除</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
MoreOutlined,
|
||||
EditOutlined,
|
||||
DeleteOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'edit'): void;
|
||||
(e: 'remove'): void;
|
||||
}>();
|
||||
|
||||
const onClick = ({ key }) => {
|
||||
emit(key);
|
||||
};
|
||||
</script>
|
||||
@@ -1,121 +0,0 @@
|
||||
<!-- 用户信息 -->
|
||||
<template>
|
||||
<a-card :bordered="false" :body-style="{ padding: '20px' }">
|
||||
<div class="ele-cell workplace-user-card">
|
||||
<div class="ele-cell-content ele-cell">
|
||||
<a-avatar :size="68" :src="loginUser.avatar">
|
||||
<template v-if="!loginUser.avatar" #icon>
|
||||
<user-outlined />
|
||||
</template>
|
||||
</a-avatar>
|
||||
<div class="ele-cell-content">
|
||||
<h4 class="ele-elip">
|
||||
早安, {{ loginUser.nickname }}, 开始您一天的工作吧!
|
||||
</h4>
|
||||
<div class="ele-elip ele-text-secondary">
|
||||
<cloud-outlined />
|
||||
<em>{{ elip[Math.floor(Math.random() * elip.length)] }}</em>
|
||||
<!-- <em>今日多云转阴,18℃ - 22℃,出门记得穿外套哦~</em>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="workplace-count-group">
|
||||
<!-- <div class="workplace-count-item">-->
|
||||
<!-- <div class="workplace-count-header">-->
|
||||
<!-- <ele-tag color="blue" shape="circle" size="small">-->
|
||||
<!-- <appstore-filled />-->
|
||||
<!-- </ele-tag>-->
|
||||
<!-- <span class="workplace-count-name">项目数</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <h2>0</h2>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="workplace-count-item">-->
|
||||
<!-- <div class="workplace-count-header">-->
|
||||
<!-- <ele-tag color="orange" shape="circle" size="small">-->
|
||||
<!-- <check-square-outlined />-->
|
||||
<!-- </ele-tag>-->
|
||||
<!-- <span class="workplace-count-name">待办项</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <h2>6 / 24</h2>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="workplace-count-item">-->
|
||||
<!-- <div class="workplace-count-header">-->
|
||||
<!-- <ele-tag color="green" shape="circle" size="small">-->
|
||||
<!-- <bell-filled />-->
|
||||
<!-- </ele-tag>-->
|
||||
<!-- <span class="workplace-count-name">消息</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <h2>0</h2>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import {
|
||||
UserOutlined,
|
||||
CloudOutlined,
|
||||
AppstoreFilled,
|
||||
CheckSquareOutlined,
|
||||
BellFilled
|
||||
} from '@ant-design/icons-vue';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 当前登录用户信息
|
||||
const loginUser = computed(() => userStore.info ?? {});
|
||||
const elip = ref<string[]>([
|
||||
'小事成就大事,细节成就完美~',
|
||||
'心态决定命运,自信走向成功',
|
||||
'人生能有几回博,今日不博何时博',
|
||||
'成功需要成本,时间也是一种成本,对时间的珍惜就是对成本的节约',
|
||||
'有志者自有千方百计,无志者只感千难万难',
|
||||
'积一时之跬步,臻千里之遥程'
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.workplace-user-card {
|
||||
.ele-cell-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.workplace-count-group {
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.workplace-count-item {
|
||||
display: inline-block;
|
||||
margin: 0 4px 0 24px;
|
||||
}
|
||||
|
||||
.workplace-count-name {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 992px) {
|
||||
.workplace-count-item {
|
||||
margin: 0 2px 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.workplace-user-card {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.workplace-count-group {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,66 +0,0 @@
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="400"
|
||||
:visible="visible"
|
||||
:title="`邀请新成员`"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
:footer="null"
|
||||
@ok="save"
|
||||
>
|
||||
<div
|
||||
class="qrcode-list"
|
||||
style="display: flex; justify-content: space-around"
|
||||
>
|
||||
<div>
|
||||
<img :src="qrcode" width="240" height="240" />
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 26px;
|
||||
padding-top: 20px;
|
||||
"
|
||||
>使用微信扫一扫</div
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</ele-modal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { User } from '@/api/system/user/model';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { taskJoinQRCode } from '@/api/oa/task';
|
||||
|
||||
defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done', user: User): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
const qrcode = ref('');
|
||||
|
||||
// 用户信息
|
||||
const form = reactive<User>({
|
||||
userId: undefined,
|
||||
nickname: undefined
|
||||
});
|
||||
|
||||
const save = () => {
|
||||
emit('done', form);
|
||||
};
|
||||
|
||||
taskJoinQRCode({}).then((text) => {
|
||||
qrcode.value = String(text);
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="less"></style>
|
||||
@@ -1,112 +0,0 @@
|
||||
<template>
|
||||
<a-card :title="title" :bordered="false" :body-style="{ padding: '2px' }">
|
||||
<template #extra
|
||||
><a @click="openUrl('/oa/task')" class="ele-text-placeholder"
|
||||
>更多<RightOutlined /></a
|
||||
></template>
|
||||
<a-list :size="`small`" :split="false" :data-source="list">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<div class="app-box">
|
||||
<div class="app-info">
|
||||
<a
|
||||
class="ele-text-secondary"
|
||||
@click="openNew('/oa/task/detail/' + item.taskId)"
|
||||
>
|
||||
<a-typography-paragraph
|
||||
ellipsis
|
||||
:content="`【${item.taskType}】${item.name}`"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<a class="ele-text-placeholder">
|
||||
<a-tag v-if="item.progress === TOBEARRANGED" color="red"
|
||||
>待安排</a-tag
|
||||
>
|
||||
<a-tag v-if="item.progress === PENDING" color="orange"
|
||||
>待处理</a-tag
|
||||
>
|
||||
<a-tag v-if="item.progress === PROCESSING" color="purple"
|
||||
>处理中</a-tag
|
||||
>
|
||||
<a-tag v-if="item.progress === TOBECONFIRMED" color="cyan"
|
||||
>待评价</a-tag
|
||||
>
|
||||
<a-tag v-if="item.progress === COMPLETED" color="green"
|
||||
>已完成</a-tag
|
||||
>
|
||||
<a-tag v-if="item.progress === CLOSED">已关闭</a-tag>
|
||||
<div class="ele-text-danger" v-if="item.overdueDays">
|
||||
已逾期{{ item.overdueDays }}天
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { Task } from '@/api/oa/task/model';
|
||||
import { pageTask } from '@/api/oa/task';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import {
|
||||
CLOSED,
|
||||
COMPLETED,
|
||||
PENDING,
|
||||
PROCESSING,
|
||||
TOBEARRANGED,
|
||||
TOBECONFIRMED
|
||||
} from '@/api/oa/task/model/progress';
|
||||
import { RightOutlined } from '@ant-design/icons-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
title: string;
|
||||
}>();
|
||||
|
||||
const list = ref<Task[]>([]);
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
const reload = () => {
|
||||
const { title } = props;
|
||||
const userStore = useUserStore();
|
||||
const where = {
|
||||
userId: undefined,
|
||||
commander: userStore.info?.userId,
|
||||
limit: 6,
|
||||
status: 0
|
||||
};
|
||||
// 加载列表
|
||||
pageTask(where).then((data) => {
|
||||
if (data?.list) {
|
||||
list.value = data.list;
|
||||
}
|
||||
});
|
||||
};
|
||||
reload();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DashboardArticleList'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.app-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
overflow: hidden;
|
||||
.app-info {
|
||||
display: flex;
|
||||
margin-left: 5px;
|
||||
flex-direction: column;
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,84 +0,0 @@
|
||||
<!-- 小组成员 -->
|
||||
<template>
|
||||
<a-card :title="title" :bordered="false" :body-style="{ padding: '2px 0px' }">
|
||||
<template #extra>
|
||||
<more-icon @remove="onRemove" @edit="onEdit" />
|
||||
</template>
|
||||
<div
|
||||
v-for="(item, index) in userList"
|
||||
:key="index"
|
||||
class="ele-cell user-list-item"
|
||||
>
|
||||
<div style="flex-shrink: 0">
|
||||
<a-avatar :size="46" :src="item.avatar" />
|
||||
</div>
|
||||
<div class="ele-cell-content">
|
||||
<span class="ele-cell-title ele-elip">{{ item.nickname }}</span>
|
||||
<div class="ele-cell-desc ele-elip">{{ item.phone }}</div>
|
||||
</div>
|
||||
<div style="flex-shrink: 0">
|
||||
<a-tag :color="['green', 'red'][item.status]">
|
||||
{{ ['在线', '离线'][item.status] }}
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import MoreIcon from './more-icon.vue';
|
||||
import { pageUsers } from '@/api/system/user';
|
||||
import type { User } from '@/api/system/user/model';
|
||||
|
||||
defineProps<{
|
||||
title?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'remove'): void;
|
||||
(e: 'edit'): void;
|
||||
}>();
|
||||
|
||||
// 小组成员数据
|
||||
const userList = ref<User[]>([]);
|
||||
|
||||
/* 查询小组成员 */
|
||||
const queryUserList = () => {
|
||||
pageUsers({ parentId: 11, limit: 5 }).then((data: any) => {
|
||||
userList.value = data.list;
|
||||
});
|
||||
};
|
||||
|
||||
const onRemove = () => {
|
||||
emit('remove');
|
||||
};
|
||||
|
||||
const onEdit = () => {
|
||||
emit('edit');
|
||||
};
|
||||
|
||||
queryUserList();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.user-list-item {
|
||||
padding: 12px 18px;
|
||||
|
||||
& + .user-list-item {
|
||||
border-top: 1px solid hsla(0, 0%, 60%, 0.15);
|
||||
}
|
||||
|
||||
.ele-cell-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ele-cell-desc {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ant-tag {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,365 +0,0 @@
|
||||
<template>
|
||||
<div class="ele-body ele-body-card">
|
||||
<a-row :gutter="16" ref="wrapRef">
|
||||
<a-col :md="6">
|
||||
<a-card hoverable>
|
||||
<div class="flex justify-between leading-5">
|
||||
<div class="title font-bold">文章</div>
|
||||
<div class="count font-bold">20</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="6">
|
||||
<a-card hoverable>
|
||||
<div class="flex justify-between leading-5">
|
||||
<div class="title font-bold">商品</div>
|
||||
<div class="count font-bold">1</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="6">
|
||||
<a-card hoverable>
|
||||
<div class="flex justify-between leading-5">
|
||||
<div class="title font-bold">用户</div>
|
||||
<div class="count font-bold">658</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :md="6">
|
||||
<a-card hoverable>
|
||||
<div class="flex justify-between leading-5">
|
||||
<div class="title font-bold">订单</div>
|
||||
<div class="count font-bold">1820</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<!-- <a-row :gutter="16" ref="wrapRef">-->
|
||||
<!-- <a-col-->
|
||||
<!-- v-for="(item, index) in data"-->
|
||||
<!-- :key="item.name"-->
|
||||
<!-- :lg="item.lg"-->
|
||||
<!-- :md="item.md"-->
|
||||
<!-- :sm="item.sm"-->
|
||||
<!-- :xs="item.xs"-->
|
||||
<!-- >-->
|
||||
<!-- <component-->
|
||||
<!-- :is="item.name"-->
|
||||
<!-- :title="item.title"-->
|
||||
<!-- @remove="onRemove(index)"-->
|
||||
<!-- @edit="onEdit(index)"-->
|
||||
<!-- />-->
|
||||
<!-- </a-col>-->
|
||||
<!-- </a-row>-->
|
||||
<!-- <a-card :bordered="false" :body-style="{ padding: 0 }">-->
|
||||
<!-- <div class="ele-cell" style="line-height: 42px">-->
|
||||
<!-- <div-->
|
||||
<!-- class="ele-cell-content ele-text-primary workplace-bottom-btn"-->
|
||||
<!-- @click="add"-->
|
||||
<!-- >-->
|
||||
<!-- <PlusCircleOutlined /> 添加视图-->
|
||||
<!-- </div>-->
|
||||
<!-- <a-divider type="vertical" />-->
|
||||
<!-- <div-->
|
||||
<!-- class="ele-cell-content ele-text-primary workplace-bottom-btn"-->
|
||||
<!-- @click="reset"-->
|
||||
<!-- >-->
|
||||
<!-- <UndoOutlined /> 重置布局-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </a-card>-->
|
||||
<ele-modal
|
||||
:width="680"
|
||||
v-model:visible="visible"
|
||||
title="未添加的视图"
|
||||
:footer="null"
|
||||
>
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-for="item in notAddedData"
|
||||
:key="item.name"
|
||||
:md="8"
|
||||
:sm="12"
|
||||
:xs="24"
|
||||
>
|
||||
<div
|
||||
class="workplace-card-item ele-border-split"
|
||||
@click="addView(item)"
|
||||
>
|
||||
<div class="workplace-card-header ele-border-split">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="workplace-card-body ele-text-placeholder">
|
||||
<plus-circle-outlined />
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-empty v-if="!notAddedData.length" description="已添加所有视图" />
|
||||
</ele-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
||||
import SortableJs from 'sortablejs';
|
||||
import type { Row as ARow } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import Article from './components/article-list.vue';
|
||||
import Link from './components/link-list.vue';
|
||||
import App from './components/app-list.vue';
|
||||
import Task from './components/task-card.vue';
|
||||
import {
|
||||
PlusCircleOutlined,
|
||||
SoundOutlined,
|
||||
RightOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { openNew } from '@/utils/common';
|
||||
const CACHE_KEY = 'workplace-layout';
|
||||
const domain = ref('');
|
||||
|
||||
import { getSiteInfo } from '@/api/layout';
|
||||
|
||||
getSiteInfo().then((data) => {
|
||||
if (data?.domain) {
|
||||
domain.value = data.domain;
|
||||
if (data.domain.indexOf('http') == 0) {
|
||||
localStorage.setItem('Domain', domain.value);
|
||||
} else {
|
||||
localStorage.setItem('Domain', `http://${domain.value}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
interface ViewItem {
|
||||
name: string;
|
||||
title: string;
|
||||
lg: number;
|
||||
md: number;
|
||||
sm: number;
|
||||
xs: number;
|
||||
}
|
||||
|
||||
// 默认布局
|
||||
const DEFAULT: ViewItem[] = [
|
||||
{
|
||||
name: 'link',
|
||||
title: '网址导航',
|
||||
lg: 24,
|
||||
md: 24,
|
||||
sm: 24,
|
||||
xs: 24
|
||||
},
|
||||
{
|
||||
name: 'task-card',
|
||||
title: '我的工单',
|
||||
lg: 18,
|
||||
md: 24,
|
||||
sm: 24,
|
||||
xs: 24
|
||||
},
|
||||
// {
|
||||
// name: 'project-card',
|
||||
// title: '项目管理',
|
||||
// lg: 16,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// },
|
||||
{
|
||||
name: 'user-list',
|
||||
title: '小组成员',
|
||||
lg: 6,
|
||||
md: 24,
|
||||
sm: 24,
|
||||
xs: 24
|
||||
}
|
||||
// {
|
||||
// name: 'activities-card',
|
||||
// title: '最新动态',
|
||||
// lg: 6,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// },
|
||||
// {
|
||||
// name: 'goal-card',
|
||||
// title: '本月目标',
|
||||
// lg: 8,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// },
|
||||
// {
|
||||
// name: 'docs',
|
||||
// title: '知识库',
|
||||
// lg: 8,
|
||||
// md: 24,
|
||||
// sm: 24,
|
||||
// xs: 24
|
||||
// }
|
||||
];
|
||||
|
||||
// 获取缓存的顺序
|
||||
const cache = (() => {
|
||||
const str = localStorage.getItem(CACHE_KEY);
|
||||
try {
|
||||
return str ? JSON.parse(str) : null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
const data = ref<ViewItem[]>([...(cache ?? DEFAULT)]);
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
const wrapRef = ref<InstanceType<typeof ARow> | null>(null);
|
||||
|
||||
let sortableIns: SortableJs | null = null;
|
||||
|
||||
// 未添加的数据
|
||||
const notAddedData = computed(() => {
|
||||
return DEFAULT.filter((d) => !data.value.some((t) => t.name === d.name));
|
||||
});
|
||||
|
||||
/* 添加 */
|
||||
const add = () => {
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
/* 重置布局 */
|
||||
const reset = () => {
|
||||
data.value = [...DEFAULT];
|
||||
cacheData();
|
||||
message.success('已重置');
|
||||
};
|
||||
|
||||
/* 缓存布局 */
|
||||
const cacheData = () => {
|
||||
localStorage.setItem(CACHE_KEY, JSON.stringify(data.value));
|
||||
};
|
||||
|
||||
/* 删除视图 */
|
||||
const onRemove = (index: number) => {
|
||||
data.value = data.value.filter((_d, i) => i !== index);
|
||||
cacheData();
|
||||
};
|
||||
|
||||
/* 编辑视图 */
|
||||
const onEdit = (index: number) => {
|
||||
data.value.map((d) => {
|
||||
if (d.name == 'user-list') {
|
||||
}
|
||||
});
|
||||
// message.info('点击了编辑');
|
||||
};
|
||||
|
||||
/* 添加视图 */
|
||||
const addView = (item) => {
|
||||
data.value.push(item);
|
||||
cacheData();
|
||||
message.success('已添加');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const isTouchDevice = 'ontouchstart' in document.documentElement;
|
||||
if (isTouchDevice) {
|
||||
return;
|
||||
}
|
||||
sortableIns = new SortableJs(wrapRef.value?.$el, {
|
||||
handle: '.ant-card-head',
|
||||
animation: 300,
|
||||
onUpdate: ({ oldIndex, newIndex }) => {
|
||||
if (typeof oldIndex === 'number' && typeof newIndex === 'number') {
|
||||
const temp = [...data.value];
|
||||
temp.splice(newIndex, 0, temp.splice(oldIndex, 1)[0]);
|
||||
data.value = temp;
|
||||
cacheData();
|
||||
}
|
||||
},
|
||||
setData: () => {}
|
||||
});
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (sortableIns) {
|
||||
sortableIns.destroy();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import ActivitiesCard from './components/activities-card.vue';
|
||||
import TaskCard from './components/task-card.vue';
|
||||
import GoalCard from './components/goal-card.vue';
|
||||
import UserList from './components/count-user.vue';
|
||||
import Docs from './components/docs.vue';
|
||||
import LinkCard from './components/link-card.vue';
|
||||
import ProfileCard from './components/profile-card.vue';
|
||||
|
||||
export default {
|
||||
name: 'DashboardWorkplace',
|
||||
components: {
|
||||
LinkCard,
|
||||
UserList,
|
||||
ActivitiesCard,
|
||||
TaskCard,
|
||||
GoalCard,
|
||||
Docs,
|
||||
ProfileCard
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ele-body :deep(.ant-card-head) {
|
||||
cursor: move;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ele-body :deep(.ant-row > .ant-col.sortable-chosen > .ant-card) {
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.workplace-bottom-btn {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.workplace-bottom-btn:hover {
|
||||
background: hsla(0, 0%, 60%, 0.05);
|
||||
}
|
||||
|
||||
/* 添加弹窗 */
|
||||
.workplace-card-item {
|
||||
margin-bottom: 15px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: box-shadow 0.2s, background-color 0.2s;
|
||||
}
|
||||
|
||||
.workplace-card-item:hover {
|
||||
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1);
|
||||
background: hsla(0, 0%, 60%, 0.05);
|
||||
}
|
||||
|
||||
.workplace-card-item .workplace-card-header {
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
padding: 8px;
|
||||
}
|
||||
.gg-title {
|
||||
padding: 0 5px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.workplace-card-body {
|
||||
font-size: 26px;
|
||||
padding: 24px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -22,17 +22,12 @@
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<a
|
||||
@click="
|
||||
openPreview(
|
||||
`https://${record.tenantId}.wsdns.cn${record.path}`
|
||||
)
|
||||
"
|
||||
>{{ record.name }}</a
|
||||
>
|
||||
<a @click="openUrl(`${domain}${record.path}`)">{{
|
||||
record.name
|
||||
}}</a>
|
||||
</template>
|
||||
<template v-if="column.key === 'path'">
|
||||
<span class="ele-text-placeholder">{{ record.path }}</span>
|
||||
<span class="ele-text-placeholder">{{ `${record.path}` }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'component'">
|
||||
<span class="ele-text-placeholder">{{ record.component }}</span>
|
||||
@@ -70,7 +65,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref } from 'vue';
|
||||
import { createVNode, ref, watch } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
@@ -87,7 +82,8 @@
|
||||
} from '@/api/cms/design';
|
||||
import type { Design, DesignParam } from '@/api/cms/design/model';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { openPreview } from '@/utils/common';
|
||||
import { openPreview, openUrl } from "@/utils/common";
|
||||
import { getSiteDomain } from '@/utils/domain';
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
@@ -100,7 +96,8 @@
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
const domain = localStorage.getItem('Domain');
|
||||
// 网站域名
|
||||
const domain = getSiteDomain();
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(true);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="site_name"
|
||||
:disabled="isUpdate"
|
||||
v-model:value="form.name"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
261
src/views/cms/link/components/link-edit.vue
Normal file
261
src/views/cms/link/components/link-edit.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
<!-- 链接编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="460"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate ? '修改链接' : '添加链接'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 5, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="名称" name="name">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="50"
|
||||
placeholder="链接名称"
|
||||
v-model:value="form.name"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="网址" name="url">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="200"
|
||||
placeholder="https://www.baidu.com"
|
||||
v-model:value="form.url"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="类型" name="linkType">
|
||||
<a-select
|
||||
optionFilterProp="label"
|
||||
placeholder="请选择链接类型"
|
||||
:options="linkType"
|
||||
v-model:value="form.linkType"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图标" name="icon">
|
||||
<SelectFile
|
||||
:placeholder="`请选择视频文件`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
<!-- <ele-image-upload-->
|
||||
<!-- v-model:value="images"-->
|
||||
<!-- :limit="1"-->
|
||||
<!-- :item-style="{ width: '40px', height: '40px' }"-->
|
||||
<!-- :upload-handler="uploadHandler"-->
|
||||
<!-- @upload="onUpload"-->
|
||||
<!-- />-->
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="99999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="备注">
|
||||
<a-textarea
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
placeholder="请输入备注"
|
||||
v-model:value="form.comments"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import type { FormInstance, Rule } from 'ant-design-vue/es/form';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import { addLink, updateLink } from '@/api/cms/link';
|
||||
import type { Link } from '@/api/cms/link/model';
|
||||
import { urlReg } from 'ele-admin-pro';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { uploadFile } from '@/api/system/file';
|
||||
import { getDictionaryOptions } from '@/utils/common';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Link | null;
|
||||
}>();
|
||||
|
||||
//
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
// 链接类型
|
||||
const linkType = getDictionaryOptions('linkType');
|
||||
// 已上传数据
|
||||
const images = ref<ItemType[]>([]);
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const { form, resetFields, assignFields } = useFormData<Link>({
|
||||
id: undefined,
|
||||
name: '',
|
||||
url: '',
|
||||
sortNumber: 100,
|
||||
linkType: undefined,
|
||||
comments: undefined
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<Record<string, Rule[]>>({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
linkType: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
url: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入正确的URL',
|
||||
type: 'string',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 上传文件
|
||||
const onUpload = (item: any) => {
|
||||
const { file } = item;
|
||||
uploadFile(file)
|
||||
.then((data) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.url,
|
||||
status: 'done'
|
||||
});
|
||||
form.icon = data.url;
|
||||
message.success('上传成功');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 上传事件 */
|
||||
const uploadHandler = (file: File) => {
|
||||
const item: ItemType = {
|
||||
file,
|
||||
uid: (file as any).uid,
|
||||
name: file.name
|
||||
};
|
||||
if (file.size / 1024 / 1024 > 1) {
|
||||
message.error('大小不能超过 1MB');
|
||||
return;
|
||||
}
|
||||
onUpload(item);
|
||||
};
|
||||
|
||||
const chooseFile = (data: FileRecord) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.path,
|
||||
status: 'done'
|
||||
});
|
||||
form.icon = data.path;
|
||||
};
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index, 1);
|
||||
form.icon = '';
|
||||
};
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const saveOrUpdate = isUpdate.value ? updateLink : addLink;
|
||||
saveOrUpdate(form)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
assignFields(props.data);
|
||||
images.value = [];
|
||||
if (props.data.icon) {
|
||||
images.value.push({
|
||||
uid: 1,
|
||||
url: props.data.icon,
|
||||
status: 'done'
|
||||
});
|
||||
}
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
images.value = [];
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
106
src/views/cms/link/components/link-search.vue
Normal file
106
src/views/cms/link/components/link-search.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<a-space style="flex-wrap: wrap">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
<span>添加网址</span>
|
||||
</a-button>
|
||||
<DictSelect
|
||||
dict-code="linkType"
|
||||
v-model:value="where.linkType"
|
||||
:placeholder="`选择分类`"
|
||||
style="width: 200px"
|
||||
@change="search"
|
||||
/>
|
||||
<a-input-search
|
||||
allow-clear
|
||||
placeholder="搜索关键词"
|
||||
v-model:value="where.keywords"
|
||||
@pressEnter="search"
|
||||
@search="search"
|
||||
/>
|
||||
<a-button @click="reset">重置</a-button>
|
||||
<a-button
|
||||
v-if="hasRole('superAdmin')"
|
||||
@click="openUrl(`/website/link/dict`)"
|
||||
>设置分类</a-button
|
||||
>
|
||||
</a-space>
|
||||
<a-space :size="10" style="flex-wrap: wrap; margin-right: 20px">
|
||||
<a-button
|
||||
danger
|
||||
v-if="selection?.length"
|
||||
class="ele-btn-icon"
|
||||
@click="removeBatch"
|
||||
>
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
<span>批量删除</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { watch } from 'vue';
|
||||
import DictSelect from '@/components/DictSelect/index.vue';
|
||||
import { LinkParam } from '@/api/cms/link/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { hasRole } from '@/utils/permission';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: LinkParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
(e: 'advanced'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where, resetFields } = useSearch<LinkParam>({
|
||||
linkType: undefined,
|
||||
name: undefined,
|
||||
keywords: ''
|
||||
});
|
||||
|
||||
/* 搜索 */
|
||||
const search = () => {
|
||||
emit('search', {
|
||||
...where
|
||||
});
|
||||
};
|
||||
|
||||
// 发布应用
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
// 批量删除
|
||||
const removeBatch = () => {
|
||||
emit('remove');
|
||||
};
|
||||
|
||||
/* 重置 */
|
||||
const reset = () => {
|
||||
resetFields();
|
||||
search();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
@@ -4,7 +4,7 @@
|
||||
:width="460"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:title="isUpdate ? '修改位置' : '添加位置'"
|
||||
:title="isUpdate ? '修改分类' : '添加分类'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
@@ -20,7 +20,6 @@
|
||||
>
|
||||
<a-form-item label="分类标识" name="dictCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="20"
|
||||
disabled
|
||||
placeholder="请输入分类标识"
|
||||
@@ -65,7 +64,7 @@
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import { addDictData, updateDictData } from '@/api/system/dict-data';
|
||||
import { DictData } from '@/api/system/dict-data/model';
|
||||
import { removeSiteInfoCache } from "@/api/cms/website";
|
||||
import { removeSiteInfoCache } from '@/api/cms/website';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
@@ -97,9 +96,10 @@
|
||||
// 表单数据
|
||||
const { form, resetFields, assignFields } = useFormData<DictData>({
|
||||
dictId: undefined,
|
||||
dictName: '',
|
||||
dictDataId: undefined,
|
||||
dictDataName: '',
|
||||
dictCode: 'mpPosition',
|
||||
dictCode: 'linkType',
|
||||
dictDataCode: '',
|
||||
sortNumber: 100,
|
||||
comments: ''
|
||||
@@ -137,11 +137,12 @@
|
||||
const saveOrUpdate = isUpdate.value ? updateDictData : addDictData;
|
||||
form.dictDataCode = form.dictDataName;
|
||||
form.dictId = props.dictId;
|
||||
form.dictName = '链接分类';
|
||||
saveOrUpdate(form)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
// 清除字典缓存
|
||||
// 清除缓存
|
||||
removeSiteInfoCache(form.dictCode + ':' + form.tenantId);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
@@ -85,7 +85,7 @@
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
title: '分类名称',
|
||||
dataIndex: 'dictDataName',
|
||||
showSorterTooltip: false
|
||||
},
|
||||
@@ -120,7 +120,7 @@
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
|
||||
where.dictCode = 'mpPosition';
|
||||
where.dictCode = 'linkType';
|
||||
return pageDictData({ ...where, ...orders, page, limit });
|
||||
};
|
||||
|
||||
@@ -180,11 +180,11 @@
|
||||
|
||||
// 初始化字典
|
||||
const loadDict = () => {
|
||||
listDictionaries({ dictCode: 'mpPosition' }).then(async (data) => {
|
||||
listDictionaries({ dictCode: 'linkType' }).then(async (data) => {
|
||||
if (data?.length == 0) {
|
||||
await addDict({ dictCode: 'mpPosition', dictName: '菜单位置' });
|
||||
await addDict({ dictCode: 'linkType', dictName: '链接分类' });
|
||||
}
|
||||
await listDictionaries({ dictCode: 'mpPosition' }).then((list) => {
|
||||
await listDictionaries({ dictCode: 'linkType' }).then((list) => {
|
||||
list?.map((d) => {
|
||||
dictId.value = Number(d.dictId);
|
||||
});
|
||||
@@ -206,6 +206,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'GroupIdDict'
|
||||
name: 'TaskDictData'
|
||||
};
|
||||
</script>
|
||||
164
src/views/cms/link/index.vue
Normal file
164
src/views/cms/link/index.vue
Normal file
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="ele-body">
|
||||
<a-card :bordered="false">
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
:scroll="{ x: 800 }"
|
||||
cache-key="proSystemLinkTable"
|
||||
>
|
||||
<template #toolbar>
|
||||
<LinkSearch @add="openEdit" @search="reload" />
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'link'">
|
||||
<a-avatar
|
||||
shape="square"
|
||||
:width="40"
|
||||
:src="record.icon"
|
||||
style="margin-right: 5px"
|
||||
/>
|
||||
<a @click="openUrl(record.url)">{{ record.name }}</a>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click.stop="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
placement="topRight"
|
||||
title="确定要删除此模块吗?"
|
||||
@confirm.stop="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
<!-- 编辑弹窗 -->
|
||||
<link-edit v-model:visible="showEdit" :data="current" @done="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import { AntDesignOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro/es';
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import { messageLoading } from 'ele-admin-pro/es';
|
||||
import LinkEdit from './components/link-edit.vue';
|
||||
import { pageLink, removeLink } from '@/api/cms/link';
|
||||
import type { Link, LinkParam } from '@/api/cms/link/model';
|
||||
import { Menu } from '@/api/system/menu/model';
|
||||
import { openNew, openUrl } from '@/utils/common';
|
||||
import LinkSearch from './components/link-search.vue';
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
// {
|
||||
// key: 'index',
|
||||
// width: 48,
|
||||
// align: 'center',
|
||||
// fixed: 'left',
|
||||
// hideInSetting: true,
|
||||
// customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
|
||||
// },
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'link',
|
||||
key: 'link'
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'comments'
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
align: 'center',
|
||||
width: 120,
|
||||
showSorterTooltip: false,
|
||||
customRender: ({ text }) => ['显示', '隐藏'][text]
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 200,
|
||||
align: 'center'
|
||||
}
|
||||
]);
|
||||
|
||||
// 表格选中数据
|
||||
const selection = ref<Link[]>([]);
|
||||
|
||||
// 当前编辑数据
|
||||
const current = ref<Link | null>(null);
|
||||
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
|
||||
return pageLink({ ...where, ...orders, limit, page });
|
||||
};
|
||||
|
||||
/* 搜索 */
|
||||
const reload = (where?: LinkParam) => {
|
||||
selection.value = [];
|
||||
tableRef?.value?.reload({ page: 1, where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: Link) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 自定义行属性 */
|
||||
const customRow = (record: Link) => {
|
||||
return {
|
||||
// 行点击事件
|
||||
onClick: () => {
|
||||
// openUrl(`${record.url}`);
|
||||
},
|
||||
// 行双击事件
|
||||
onDblclick: () => {
|
||||
openUrl(`${record.url}`);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: Link) => {
|
||||
const hide = messageLoading('请求中..', 0);
|
||||
removeLink(row.id)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'SystemLink'
|
||||
};
|
||||
</script>
|
||||
@@ -68,7 +68,7 @@
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label="位置" name="pageId">
|
||||
<SelectMpAd
|
||||
<SelectMpPages
|
||||
:placeholder="`请选择页面`"
|
||||
v-model:value="form.pageName"
|
||||
@done="choosePageId"
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
:need-page="false"
|
||||
tool-class="ele-toolbar-form"
|
||||
class="sys-org-table"
|
||||
>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="site_name"
|
||||
:disabled="isUpdate"
|
||||
v-model:value="form.name"
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -33,7 +32,7 @@
|
||||
v-if="!pageId"
|
||||
extra="页面参数,留空视为全局参数"
|
||||
>
|
||||
<SelectMpAd
|
||||
<SelectMpPages
|
||||
:placeholder="`请选择页面`"
|
||||
v-model:value="form.pageName"
|
||||
@done="choosePageId"
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '编辑菜单' : '添加菜单'"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="路由地址" name="path">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入路由地址"
|
||||
v-model:value="form.path"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图标颜色" name="color">
|
||||
<ele-color-picker
|
||||
:show-alpha="true"
|
||||
v-model:value="form.color"
|
||||
:predefine="predefineColors"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="打开方式" name="target">
|
||||
<DictSelect
|
||||
dict-code="navType"
|
||||
class="form-item"
|
||||
placeholder="请选择链接方式"
|
||||
v-model:value="form.target"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所在行" name="rows">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="3"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入所在行"
|
||||
v-model:value="form.rows"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="管理人员可见" name="adminShow">
|
||||
<a-switch
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
:checked="form.adminShow === 1"
|
||||
@update:checked="updateAdminShow"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject, uuid } from 'ele-admin-pro';
|
||||
import { addMpMenu, updateMpMenu } from '@/api/cms/mp-menu';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 类型 0服务 1订单
|
||||
type?: number;
|
||||
// 修改回显的数据
|
||||
data?: MpMenu | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否显示最大化切换按钮
|
||||
const maxable = ref(true);
|
||||
// 表格选中数据
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const images = ref<ItemType[]>([]);
|
||||
|
||||
// 用户信息
|
||||
const form = reactive<MpMenu>({
|
||||
menuId: undefined,
|
||||
parentId: 0,
|
||||
title: '',
|
||||
type: 2,
|
||||
isMpWeixin: true,
|
||||
path: undefined,
|
||||
component: undefined,
|
||||
target: undefined,
|
||||
icon: '',
|
||||
color: undefined,
|
||||
hide: undefined,
|
||||
position: undefined,
|
||||
rows: undefined,
|
||||
active: undefined,
|
||||
userId: 0,
|
||||
adminShow: undefined,
|
||||
home: undefined,
|
||||
sortNumber: 100,
|
||||
comments: '',
|
||||
status: 0
|
||||
});
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写菜单名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
path: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写路由地址',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const chooseFile = (data: FileRecord) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.thumbnail,
|
||||
status: 'done'
|
||||
});
|
||||
form.icon = data.thumbnail;
|
||||
};
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index, 1);
|
||||
form.icon = '';
|
||||
};
|
||||
|
||||
const updateAdminShow = (value: boolean) => {
|
||||
form.adminShow = value ? 1 : 0;
|
||||
};
|
||||
|
||||
// 预设颜色
|
||||
const predefineColors = ref([
|
||||
'#40a9ff',
|
||||
'#9254de',
|
||||
'#36cfc9',
|
||||
'#73d13d',
|
||||
'#f759ab',
|
||||
'#cf1313',
|
||||
'#ff4d4f',
|
||||
'#ffa940',
|
||||
'#ffc53d',
|
||||
'#f3d3d3',
|
||||
'#1b1b1b',
|
||||
'#363636',
|
||||
'#4d4d4d',
|
||||
'#737373',
|
||||
'#a6a6a6',
|
||||
'#d9d9d9',
|
||||
'#e6e6e6',
|
||||
'#f2f2f2',
|
||||
'#f7f7f7',
|
||||
'#fafafa'
|
||||
]);
|
||||
|
||||
const { resetFields } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateMpMenu : addMpMenu;
|
||||
saveOrUpdate(formData)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
images.value = [];
|
||||
if (props.type) {
|
||||
form.type = props.type;
|
||||
}
|
||||
if (props.data) {
|
||||
assignObject(form, props.data);
|
||||
if (props.data.icon) {
|
||||
images.value.push({
|
||||
uid: uuid(),
|
||||
url: props.data.icon,
|
||||
status: 'done'
|
||||
});
|
||||
}
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,52 +0,0 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
<span>添加</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { watch } from 'vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: MpMenuParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
(e: 'batchMove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where } = useSearch<MpMenu>({
|
||||
type: 0
|
||||
});
|
||||
|
||||
const handleSearch = () => {
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
@@ -1,550 +0,0 @@
|
||||
<template>
|
||||
<div class="phone-layout" v-if="form">
|
||||
<div class="phone-header-black ele-fluid">
|
||||
<div class="title ele-fluid">
|
||||
<div class="title-bar">
|
||||
<span class="back"></span>
|
||||
<span>{{ form.pageName || '个人中心' }}</span>
|
||||
<a class="share" @click="onShare"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 会员信息卡片 -->
|
||||
<template v-if="form.showUserCard">
|
||||
<a-popover>
|
||||
<template #content> 点击更换背景 </template>
|
||||
<div
|
||||
class="user-card"
|
||||
:style="{
|
||||
backgroundImage: 'url(' + param.mp_user_top + ')'
|
||||
}"
|
||||
@click="
|
||||
openUserCard({
|
||||
name: 'mp_user_top',
|
||||
value: param.mp_user_top,
|
||||
comments: '小程序我的顶部背景图片',
|
||||
sortNumber: 100
|
||||
})
|
||||
"
|
||||
>
|
||||
<div class="user-avatar" @click.stop="onAvatar">
|
||||
<a-avatar :src="param.site_logo" :size="60" />
|
||||
<div class="user-info">
|
||||
<div class="nickname">昵称</div>
|
||||
<div class="phone">手机号码</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-popover>
|
||||
<UserCardEdit
|
||||
v-model:visible="showUserCardEdit"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
<!-- 订单卡片 -->
|
||||
<template v-if="form.showOrderCard">
|
||||
<div class="order-card ele-cell">
|
||||
<div
|
||||
v-for="(item, index) in order"
|
||||
:key="index"
|
||||
class="ele-cell-content ele-text-center btn-center"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-image :src="item.icon" :width="30" :preview="false" />
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="form.showToolsCard">
|
||||
<div class="tools-card">
|
||||
<div
|
||||
v-for="(item, index) in server"
|
||||
:key="index"
|
||||
class="ele-cell"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-avatar :src="item.icon" :size="24" />
|
||||
<div
|
||||
class="title ele-cell-content"
|
||||
:style="{ color: item.color || '#333333' }"
|
||||
>{{ item.title }}</div
|
||||
>
|
||||
<RightOutlined class="ele-text-secondary" />
|
||||
</div>
|
||||
</div>
|
||||
<MpMenuEdit
|
||||
v-model:visible="showMpMenuEdit"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="form.showMenuCard">
|
||||
<div class="phone-body" style="overflow-y: auto; overflow-x: hidden">
|
||||
<!-- 幻灯片轮播 -->
|
||||
<template v-if="form.showCarousel">
|
||||
<a-carousel arrows autoplay :dots="true">
|
||||
<template v-if="adImageList">
|
||||
<template v-for="(img, index) in adImageList" :key="index">
|
||||
<div class="ad-item">
|
||||
<a-image
|
||||
:preview="false"
|
||||
:src="img.url"
|
||||
width="100%"
|
||||
height="200px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-carousel>
|
||||
</template>
|
||||
<!-- 导航菜单 -->
|
||||
<template v-if="form.showMenuCard">
|
||||
<div class="menu-card ele-cell">
|
||||
<div
|
||||
v-for="(item, index) in scrollList"
|
||||
:key="index"
|
||||
class="ele-cell-content ele-text-center btn-center"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-image :src="item.icon" :width="30" :preview="false" />
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<MpMenuEdit
|
||||
v-model:visible="showMpMenuEdit"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
<!-- 商户列表 -->
|
||||
<template v-if="form.showShopCard">
|
||||
<div class="merchant-card-title">场地预定</div>
|
||||
<div
|
||||
class="merchant-card ele-cell"
|
||||
v-for="(item, index) in shopList"
|
||||
:key="index"
|
||||
>
|
||||
<a-image :src="item.image" :width="96" :preview="false" />
|
||||
<div class="merchant-info ele-cell-content">
|
||||
<div class="merchant-name">{{ item.merchantName }}</div>
|
||||
<div class="merchant-desc ele-cell-desc">
|
||||
{{ item.comments }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="">-->
|
||||
<!-- <a-button>我要去</a-button>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
<!-- 培训课程 -->
|
||||
<template v-if="form.showTtrainCard">
|
||||
<div class="merchant-card-title">培训课程</div>
|
||||
<div
|
||||
class="merchant-card ele-cell"
|
||||
v-for="(item, index) in shopList"
|
||||
:key="index"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-image :src="item.image" :width="96" :preview="false" />
|
||||
<div class="merchant-info ele-cell-content">
|
||||
<div class="merchant-name">{{ item.merchantName }}</div>
|
||||
<div class="merchant-desc ele-cell-desc">
|
||||
{{ item.comments }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<a-card
|
||||
class="buy-bar"
|
||||
:bordered="false"
|
||||
:body-style="{ padding: '12px 16px' }"
|
||||
>
|
||||
<div class="ele-cell">
|
||||
<a
|
||||
class="home-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/home`)"
|
||||
>
|
||||
<HomeOutlined class="icon" />
|
||||
<span>首页</span>
|
||||
</a>
|
||||
<a
|
||||
class="shop-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/shop`)"
|
||||
>
|
||||
<ShopOutlined class="icon" />
|
||||
<span>商城</span>
|
||||
</a>
|
||||
<a
|
||||
class="order-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/order`)"
|
||||
>
|
||||
<ProfileOutlined class="icon" />
|
||||
<span>订单</span>
|
||||
</a>
|
||||
<a
|
||||
class="user-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/user`)"
|
||||
>
|
||||
<UserOutlined class="icon ele-text-danger" />
|
||||
<span class="ele-text-danger">我的</span>
|
||||
</a>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ProfileOutlined,
|
||||
ShopOutlined,
|
||||
HomeOutlined,
|
||||
UserOutlined,
|
||||
RightOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { MpWeixinParam, WebsiteField } from '@/api/cms/website/field/model';
|
||||
import { listWebsiteField } from '@/api/system/website/field';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { listMpMenu } from '@/api/cms/mp-menu';
|
||||
import { listAd } from '@/api/cms/ad';
|
||||
import { listMerchant } from '@/api/shop/merchant';
|
||||
import { Merchant } from '@/api/shop/merchant/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { useRouter } from 'vue-router';
|
||||
const { currentRoute } = useRouter();
|
||||
const { query } = unref(currentRoute);
|
||||
|
||||
import MpMenuEdit from './mpMenuEdit.vue';
|
||||
import UserCardEdit from '@/views/cms/field/components/website-field-edit.vue';
|
||||
|
||||
const prpos = withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
form?: any | null;
|
||||
type?: number;
|
||||
list?: any[] | null;
|
||||
refresh?: boolean;
|
||||
}>(),
|
||||
{
|
||||
placeholder: undefined
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const param = ref<MpWeixinParam>({});
|
||||
const showUserCardEdit = ref(false);
|
||||
const showMpMenuEdit = ref(false);
|
||||
// 当前编辑数据
|
||||
const current = ref<WebsiteField | null>(null);
|
||||
|
||||
// 幻灯片广告
|
||||
const adImageList = ref<any[]>();
|
||||
// 首页导航图标
|
||||
const scrollList = ref<any[]>();
|
||||
// 订单图标
|
||||
const order = ref<any[]>();
|
||||
// 服务图标
|
||||
const server = ref<any[]>();
|
||||
// 商户列表
|
||||
const shopList = ref<Merchant[]>();
|
||||
|
||||
const config = ref({
|
||||
selector: '#content', //容器,可使用css选择器
|
||||
branding: false,
|
||||
language: 'zh_CN', //调用放在langs文件夹内的语言包
|
||||
toolbar: false, //隐藏工具栏
|
||||
menubar: false, //隐藏菜单栏
|
||||
inline: true, //开启内联模式
|
||||
plugins: [] //选择需加载的插件
|
||||
//选中时出现的快捷工具,与插件有依赖关系
|
||||
// quickbars_selection_toolbar: 'bold italic forecolor | link blockquote quickimage',
|
||||
// init_instance_callback: function (editor) {
|
||||
// editor.setContent('这里是你的内容字符串');
|
||||
// }
|
||||
});
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openUserCard = (row?: WebsiteField) => {
|
||||
current.value = row ?? null;
|
||||
showUserCardEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openMpMenuEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showMpMenuEdit.value = true;
|
||||
};
|
||||
|
||||
const onShare = () => {};
|
||||
|
||||
const onAvatar = () => {};
|
||||
|
||||
const reload = () => {
|
||||
listWebsiteField({}).then((list) => {
|
||||
list.map((d) => {
|
||||
const key = String(d.name);
|
||||
param.value[key] = d.value;
|
||||
});
|
||||
});
|
||||
|
||||
listMpMenu({ type: 4 }).then((list) => {
|
||||
server.value = list.filter((d) => d.type == 0);
|
||||
order.value = list.filter((d) => d.rows == 0);
|
||||
scrollList.value = list.filter((d) => d.type == 2);
|
||||
});
|
||||
|
||||
listAd({ adType: '幻灯片' }).then((res) => {
|
||||
const carouselImages = res[0].images;
|
||||
if (carouselImages) {
|
||||
adImageList.value = JSON.parse(carouselImages);
|
||||
}
|
||||
});
|
||||
|
||||
listMerchant({}).then((list) => {
|
||||
shopList.value = list;
|
||||
});
|
||||
emit('done');
|
||||
};
|
||||
|
||||
reload();
|
||||
|
||||
watch(
|
||||
() => prpos.refresh,
|
||||
(refresh) => {
|
||||
if (refresh) {
|
||||
reload();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.phone-layout {
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
width: 390px;
|
||||
height: 844px;
|
||||
background: url('@/assets/img/app-ui.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: top;
|
||||
background-size: 100%;
|
||||
//position: relative;
|
||||
padding: 0 16px;
|
||||
.phone-header-black {
|
||||
height: 99px;
|
||||
border-radius: 20px 20px 0 0;
|
||||
background-size: 100%;
|
||||
.title {
|
||||
height: 99px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
padding-bottom: 13px;
|
||||
.title-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.back {
|
||||
display: block;
|
||||
width: 50px;
|
||||
margin-left: 3px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.share {
|
||||
display: block;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.phone-body-bg {
|
||||
padding: 0 16px;
|
||||
height: 680px;
|
||||
border-radius: 0 0 44px 44px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.phone-body {
|
||||
width: 356px;
|
||||
margin-left: 17px;
|
||||
height: 630px;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 98px;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
.comments {
|
||||
padding: 20px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.form-data {
|
||||
padding: 10px 20px;
|
||||
.submit-btn {
|
||||
padding: 30px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.goods {
|
||||
.price {
|
||||
font-size: 18px;
|
||||
}
|
||||
.ele-cell-title {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
.goods-attr {
|
||||
}
|
||||
}
|
||||
.goods-divider {
|
||||
height: 6px;
|
||||
}
|
||||
.buy-bar {
|
||||
position: fixed;
|
||||
width: 356px;
|
||||
bottom: 70px;
|
||||
//top: 881px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0 0 44px 44px;
|
||||
border-top: 1px solid var(--grey-8);
|
||||
.ele-cell-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.shop-btn,
|
||||
.kefu-btn,
|
||||
.star-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 13px;
|
||||
padding: 0 9px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.icon {
|
||||
font-size: 19px;
|
||||
}
|
||||
.buy-btn {
|
||||
display: flex;
|
||||
.add-cart {
|
||||
border-radius: 100px 0 0 100px;
|
||||
border: none;
|
||||
background-color: var(--orange-5);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
.buy-now {
|
||||
border-radius: 0 100px 100px 0;
|
||||
border: none;
|
||||
background-color: var(--red-6);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.slick-slide) {
|
||||
overflow: hidden;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
font-size: 38px;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
color: #fff;
|
||||
background-color: rgba(31, 45, 61, 0.11);
|
||||
transition: ease all 0.3s;
|
||||
opacity: 0.3;
|
||||
z-index: 1;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:before) {
|
||||
display: none;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:hover) {
|
||||
color: #fff;
|
||||
opacity: 0.5;
|
||||
}
|
||||
:deep(.slick-slide h3) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
height: 170px;
|
||||
margin: 0 1px;
|
||||
background-color: var(--grey-10);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
.user-avatar {
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.user-info {
|
||||
margin-left: 10px;
|
||||
.nickname {
|
||||
color: var(--grey-3);
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.phone {
|
||||
color: var(--grey-5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.order-card {
|
||||
width: 340px;
|
||||
height: 80px;
|
||||
margin: 0 1px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
position: absolute;
|
||||
top: 230px;
|
||||
left: 24px;
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.tools-card {
|
||||
width: 340px;
|
||||
margin: 0 1px;
|
||||
padding: 6px 16px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
position: absolute;
|
||||
top: 324px;
|
||||
left: 24px;
|
||||
.ele-cell {
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid var(--grey-9);
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,296 +0,0 @@
|
||||
<template>
|
||||
<a-page-header :title="title" @back="() => $router.go(-1)">
|
||||
<div class="ele-cell ele-cell-align-top">
|
||||
<!-- 设计画布 -->
|
||||
<div class="body ele-cell-content ele-bg-white" style="display: ">
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="menuId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
tool-class="ele-toolbar-form"
|
||||
class="sys-org-table"
|
||||
>
|
||||
<template #toolbar>
|
||||
<search
|
||||
@search="reload"
|
||||
:selection="selection"
|
||||
@add="openEdit"
|
||||
@remove="removeBatch"
|
||||
@batchMove="openMove"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="ele-text-secondary"
|
||||
>温馨提示:选图标可以上<a
|
||||
href="https://www.iconfont.cn"
|
||||
target="_blank"
|
||||
>阿里巴巴矢量图标库</a
|
||||
>,修改完后需要<a @click="clearSiteInfoCache">更新缓存</a
|
||||
>才会生效</div
|
||||
>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'icon'">
|
||||
<a-avatar :src="record.icon" :width="50" />
|
||||
</template>
|
||||
<template v-if="column.key === 'path'">
|
||||
<span class="ele-text-placeholder">{{ record.path }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'component'">
|
||||
<span class="ele-text-placeholder">{{ record.component }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 载入模拟器 -->
|
||||
<Simulator :form="form" :type="type" :refresh="refresh" @done="reload" />
|
||||
<!-- 中间间隙 -->
|
||||
<div style="width: 500px"></div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<MpMenuEdit
|
||||
v-model:visible="showEdit"
|
||||
:type="type"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</div>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import Search from './components/search.vue';
|
||||
import MpMenuEdit from './components/mpMenuEdit.vue';
|
||||
import Simulator from './components/simulator.vue';
|
||||
import {
|
||||
pageMpMenu,
|
||||
removeMpMenu,
|
||||
removeBatchMpMenu
|
||||
} from '@/api/cms/mp-menu';
|
||||
import type { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
import { getPageTitle, openUrl } from '@/utils/common';
|
||||
import { removeSiteInfoCache } from '@/api/cms/website';
|
||||
import success from '@/views/result/success/index.vue';
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格选中数据
|
||||
const selection = ref<MpMenu[]>([]);
|
||||
// 当前编辑数据
|
||||
const current = ref<MpMenu | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
// 页面标题
|
||||
const title = getPageTitle();
|
||||
const type = ref<number>(2);
|
||||
const refresh = ref(false);
|
||||
// 模拟器的字段
|
||||
const form = ref<any>({
|
||||
pageName: '管理中心',
|
||||
showUserCard: true,
|
||||
showOrderCard: true,
|
||||
showToolsCard: true
|
||||
});
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({
|
||||
page,
|
||||
limit,
|
||||
where,
|
||||
orders,
|
||||
filters
|
||||
}) => {
|
||||
if (filters) {
|
||||
where.status = filters.status;
|
||||
}
|
||||
where.type = 4;
|
||||
return pageMpMenu({
|
||||
...where,
|
||||
...orders,
|
||||
page,
|
||||
limit
|
||||
});
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'menuId',
|
||||
key: 'menuId',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单图标',
|
||||
dataIndex: 'icon',
|
||||
key: 'icon',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '路由地址',
|
||||
dataIndex: 'path',
|
||||
key: 'path'
|
||||
},
|
||||
// {
|
||||
// title: '分类ID',
|
||||
// dataIndex: 'type',
|
||||
// key: 'type',
|
||||
// align: 'center'
|
||||
// },
|
||||
{
|
||||
title: '所在行',
|
||||
dataIndex: 'rows',
|
||||
key: 'rows',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sortNumber',
|
||||
key: 'sortNumber',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
hideInSetting: true
|
||||
}
|
||||
]);
|
||||
|
||||
/* 搜索 */
|
||||
const reload = (where?: MpMenuParam) => {
|
||||
if (where?.type) {
|
||||
type.value = Number(where?.type);
|
||||
} else {
|
||||
type.value = 0;
|
||||
}
|
||||
refresh.value = !refresh.value;
|
||||
selection.value = [];
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
// 清除缓存
|
||||
const clearSiteInfoCache = () => {
|
||||
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId')).then(
|
||||
(msg) => {
|
||||
if (msg) {
|
||||
message.success('缓存已更新');
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: MpMenu) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeMpMenu(row.menuId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 批量删除 */
|
||||
const removeBatch = () => {
|
||||
if (!selection.value.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的记录吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchMpMenu(selection.value.map((d) => d.menuId))
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 自定义行属性 */
|
||||
const customRow = (record: MpMenu) => {
|
||||
return {
|
||||
// 行点击事件
|
||||
onClick: () => {
|
||||
// console.log(record);
|
||||
},
|
||||
// 行双击事件
|
||||
onDblclick: () => {
|
||||
openEdit(record);
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'MpMenuUser'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -1,279 +0,0 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '编辑菜单' : '添加菜单'"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="页面" name="type">
|
||||
<a-select
|
||||
v-model:value="form.type"
|
||||
placeholder="页面"
|
||||
:style="`width: 200px`"
|
||||
>
|
||||
<a-select-option :value="2">首页</a-select-option>
|
||||
<a-select-option :value="3">商城</a-select-option>
|
||||
<a-select-option :value="1">订单</a-select-option>
|
||||
<a-select-option :value="0">我的</a-select-option>
|
||||
<a-select-option :value="4">管理</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="路由地址" name="path">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入路由地址"
|
||||
v-model:value="form.path"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图标颜色" name="color">
|
||||
<ele-color-picker
|
||||
:show-alpha="true"
|
||||
v-model:value="form.color"
|
||||
:predefine="predefineColors"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所在行" name="rows">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="3"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入所在行"
|
||||
v-model:value="form.rows"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="打开方式" name="target">
|
||||
<DictSelect
|
||||
dict-code="navType"
|
||||
class="form-item"
|
||||
placeholder="请选择链接方式"
|
||||
v-model:value="form.target"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject, uuid } from 'ele-admin-pro';
|
||||
import { addMpMenu, updateMpMenu } from '@/api/cms/mp-menu';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 类型 0服务 1订单
|
||||
type?: number;
|
||||
// 修改回显的数据
|
||||
data?: MpMenu | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否显示最大化切换按钮
|
||||
const maxable = ref(true);
|
||||
// 表格选中数据
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const images = ref<ItemType[]>([]);
|
||||
|
||||
// 用户信息
|
||||
const form = reactive<MpMenu>({
|
||||
menuId: undefined,
|
||||
parentId: 0,
|
||||
title: '',
|
||||
type: 2,
|
||||
rows: 0,
|
||||
isMpWeixin: true,
|
||||
path: undefined,
|
||||
component: undefined,
|
||||
target: undefined,
|
||||
icon: '',
|
||||
color: undefined,
|
||||
hide: undefined,
|
||||
position: undefined,
|
||||
active: undefined,
|
||||
userId: 0,
|
||||
home: undefined,
|
||||
sortNumber: 100,
|
||||
comments: '',
|
||||
status: 0
|
||||
});
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写菜单名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
path: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写路由地址',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const chooseFile = (data: FileRecord) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.thumbnail,
|
||||
status: 'done'
|
||||
});
|
||||
form.icon = data.thumbnail;
|
||||
};
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index, 1);
|
||||
form.icon = '';
|
||||
};
|
||||
|
||||
// 预设颜色
|
||||
const predefineColors = ref([
|
||||
'#40a9ff',
|
||||
'#9254de',
|
||||
'#36cfc9',
|
||||
'#73d13d',
|
||||
'#f759ab',
|
||||
'#cf1313',
|
||||
'#ff4d4f',
|
||||
'#ffa940',
|
||||
'#ffc53d',
|
||||
'#f3d3d3',
|
||||
'#1b1b1b',
|
||||
'#363636',
|
||||
'#4d4d4d',
|
||||
'#737373',
|
||||
'#a6a6a6',
|
||||
'#d9d9d9',
|
||||
'#e6e6e6',
|
||||
'#f2f2f2',
|
||||
'#f7f7f7',
|
||||
'#fafafa'
|
||||
]);
|
||||
|
||||
const { resetFields } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateMpMenu : addMpMenu;
|
||||
saveOrUpdate(formData)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
images.value = [];
|
||||
if (props.type) {
|
||||
form.type = props.type;
|
||||
}
|
||||
if (props.data) {
|
||||
assignObject(form, props.data);
|
||||
if (props.data.icon) {
|
||||
images.value.push({
|
||||
uid: uuid(),
|
||||
url: props.data.icon,
|
||||
status: 'done'
|
||||
});
|
||||
}
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,58 +0,0 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
<span>添加</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: MpMenuParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
(e: 'batchMove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where } = useSearch<MpMenu>({
|
||||
type: 2
|
||||
});
|
||||
|
||||
const handleSearch = () => {
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
const menuType = ref(0);
|
||||
|
||||
const onChange = () => {
|
||||
console.log(menuType.value);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
@@ -1,573 +0,0 @@
|
||||
<template>
|
||||
<div class="phone-layout" v-if="form">
|
||||
<div class="phone-header-black ele-fluid">
|
||||
<div class="title ele-fluid">
|
||||
<div class="title-bar">
|
||||
<span class="back"></span>
|
||||
<span>{{ form.pageName || '首页' }}</span>
|
||||
<a class="share" @click="onShare"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="form.showMenuCard">
|
||||
<div class="phone-body" style="overflow-y: auto; overflow-x: hidden">
|
||||
<!-- 幻灯片轮播 -->
|
||||
<template v-if="form.showCarousel">
|
||||
<div
|
||||
class="ad-bar"
|
||||
:style="{
|
||||
backgroundImage: 'url(' + param.mp_user_top + ')'
|
||||
}"
|
||||
@click="onCarousel"
|
||||
>
|
||||
<a-carousel arrows autoplay :dots="true">
|
||||
<template v-if="adImageList">
|
||||
<template v-for="(img, index) in adImageList" :key="index">
|
||||
<div class="ad-item">
|
||||
<a-image
|
||||
:preview="false"
|
||||
:src="img.url"
|
||||
width="100%"
|
||||
height="200px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-carousel>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 导航菜单 -->
|
||||
<template v-if="form.showMenuCard">
|
||||
<!-- 单排 -->
|
||||
<div class="order-card ele-cell">
|
||||
<div
|
||||
v-for="(item, index) in navigation"
|
||||
:key="index"
|
||||
class="ele-cell-content ele-text-center btn-center"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-image :src="item.icon" :width="40" :preview="false" />
|
||||
<span style="white-space: nowrap">{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 双排 -->
|
||||
<div class="menu-card ele-cell">
|
||||
<template v-for="(item, index) in navigation1" :key="index">
|
||||
<div
|
||||
v-if="index < 4"
|
||||
class="ele-cell-content ele-text-center btn-center"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-avatar :src="item.icon" :width="30" :preview="false" />
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="menu-card ele-cell">
|
||||
<template v-for="(item, index) in navigation2" :key="index">
|
||||
<div
|
||||
v-if="index < 4"
|
||||
class="ele-cell-content ele-text-center btn-center"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-avatar :src="item.icon" :width="30" :preview="false" />
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<MpMenuEdit
|
||||
v-model:visible="showMpMenuEdit"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
<!-- 商户列表 -->
|
||||
<template v-if="form.showShopCard">
|
||||
<div class="merchant-card-title">场地预定</div>
|
||||
<div
|
||||
class="merchant-card ele-cell"
|
||||
v-for="(item, index) in shopList"
|
||||
:key="index"
|
||||
>
|
||||
<a-image :src="item.image" :width="96" :preview="false" />
|
||||
<div class="merchant-info ele-cell-content">
|
||||
<div class="merchant-name">{{ item.merchantName }}</div>
|
||||
<div class="merchant-desc ele-cell-desc">
|
||||
{{ item.comments }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="">-->
|
||||
<!-- <a-button>我要去</a-button>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
<!-- 培训课程 -->
|
||||
<template v-if="form.showTtrainCard">
|
||||
<div class="merchant-card-title">培训课程</div>
|
||||
<div
|
||||
class="merchant-card ele-cell"
|
||||
v-for="(item, index) in shopList"
|
||||
:key="index"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-image :src="item.image" :width="96" :preview="false" />
|
||||
<div class="merchant-info ele-cell-content">
|
||||
<div class="merchant-name">{{ item.merchantName }}</div>
|
||||
<div class="merchant-desc ele-cell-desc">
|
||||
{{ item.comments }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<a-card
|
||||
class="buy-bar"
|
||||
:bordered="false"
|
||||
:body-style="{ padding: '12px 16px' }"
|
||||
>
|
||||
<div class="ele-cell">
|
||||
<a
|
||||
class="home-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/home`)"
|
||||
>
|
||||
<HomeOutlined class="icon ele-text-danger" />
|
||||
<span class="ele-text-danger">首页</span>
|
||||
</a>
|
||||
<a
|
||||
class="shop-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/shop`)"
|
||||
>
|
||||
<ShopOutlined class="icon" />
|
||||
商城
|
||||
</a>
|
||||
<a
|
||||
class="order-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/order`)"
|
||||
>
|
||||
<ProfileOutlined class="icon" />
|
||||
订单
|
||||
</a>
|
||||
<a
|
||||
class="user-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/user`)"
|
||||
>
|
||||
<UserOutlined class="icon" />
|
||||
我的
|
||||
</a>
|
||||
</div>
|
||||
</a-card>
|
||||
<AdEdit v-model:visible="showAdEdit" :data="ad" @done="reload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ProfileOutlined,
|
||||
ShopOutlined,
|
||||
HomeOutlined,
|
||||
UserOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { MpWeixinParam, WebsiteField } from '@/api/cms/website/field/model';
|
||||
import { listWebsiteField } from '@/api/system/website/field';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { listMpMenu } from '@/api/cms/mp-menu';
|
||||
import { listAd } from '@/api/cms/ad';
|
||||
import { listMerchant } from '@/api/shop/merchant';
|
||||
import { Merchant } from '@/api/shop/merchant/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { useRouter } from 'vue-router';
|
||||
const { currentRoute } = useRouter();
|
||||
const { query } = unref(currentRoute);
|
||||
|
||||
import MpMenuEdit from './mpMenuEdit.vue';
|
||||
import { Ad } from '@/api/cms/ad/model';
|
||||
import AdEdit from '@/views/cms/ad/components/ad-edit.vue';
|
||||
|
||||
const prpos = withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
form?: any | null;
|
||||
type?: number;
|
||||
list?: any[] | null;
|
||||
refresh?: boolean;
|
||||
}>(),
|
||||
{
|
||||
placeholder: undefined
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const param = ref<MpWeixinParam>({});
|
||||
const showUserCardEdit = ref(false);
|
||||
const showMpMenuEdit = ref(false);
|
||||
const showAdEdit = ref(false);
|
||||
const ad = ref<Ad>();
|
||||
// 当前编辑数据
|
||||
const current = ref<WebsiteField | null>(null);
|
||||
|
||||
// 幻灯片广告
|
||||
const adImageList = ref<any[]>();
|
||||
// 首页导航图标
|
||||
const scrollList = ref<any>();
|
||||
// 单排
|
||||
const navigation = ref<MpMenu[]>();
|
||||
// 第一排
|
||||
const navigation1 = ref<any[]>();
|
||||
// 第二排
|
||||
const navigation2 = ref<any[]>();
|
||||
// 订单图标
|
||||
const order = ref<any[]>();
|
||||
// 服务图标
|
||||
const server = ref<any[]>();
|
||||
// 商户列表
|
||||
const shopList = ref<Merchant[]>();
|
||||
|
||||
const config = ref({
|
||||
selector: '#content', //容器,可使用css选择器
|
||||
branding: false,
|
||||
language: 'zh_CN', //调用放在langs文件夹内的语言包
|
||||
toolbar: false, //隐藏工具栏
|
||||
menubar: false, //隐藏菜单栏
|
||||
inline: true, //开启内联模式
|
||||
plugins: [] //选择需加载的插件
|
||||
//选中时出现的快捷工具,与插件有依赖关系
|
||||
// quickbars_selection_toolbar: 'bold italic forecolor | link blockquote quickimage',
|
||||
// init_instance_callback: function (editor) {
|
||||
// editor.setContent('这里是你的内容字符串');
|
||||
// }
|
||||
});
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openUserCard = (row?: WebsiteField) => {
|
||||
current.value = row ?? null;
|
||||
showUserCardEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openMpMenuEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showMpMenuEdit.value = true;
|
||||
};
|
||||
|
||||
const onShare = () => {};
|
||||
|
||||
const onCarousel = (row?: Ad) => {
|
||||
showAdEdit.value = true;
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
listWebsiteField({}).then((list) => {
|
||||
list.map((d) => {
|
||||
const key = String(d.name);
|
||||
param.value[key] = d.value;
|
||||
});
|
||||
});
|
||||
|
||||
listMpMenu({}).then((list) => {
|
||||
server.value = list.filter((d) => d.type == 0);
|
||||
order.value = list.filter((d) => d.type == 1);
|
||||
navigation.value = list.filter((d) => d.type == 2 && d.rows == 0);
|
||||
navigation1.value = list.filter((d) => d.type == 2 && d.rows == 1);
|
||||
navigation2.value = list.filter((d) => d.type == 2 && d.rows == 2);
|
||||
});
|
||||
|
||||
listAd({ adType: '幻灯片' }).then((res) => {
|
||||
const carouselImages = res[0].images;
|
||||
if (carouselImages) {
|
||||
adImageList.value = JSON.parse(carouselImages);
|
||||
}
|
||||
});
|
||||
|
||||
listMerchant({}).then((list) => {
|
||||
shopList.value = list;
|
||||
});
|
||||
emit('done');
|
||||
};
|
||||
|
||||
reload();
|
||||
|
||||
watch(
|
||||
() => prpos.refresh,
|
||||
(refresh) => {
|
||||
if (refresh) {
|
||||
reload();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.phone-layout {
|
||||
position: fixed;
|
||||
right: 26px;
|
||||
width: 390px;
|
||||
height: 844px;
|
||||
background: url('@/assets/img/app-ui.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: top;
|
||||
background-size: 100%;
|
||||
//position: relative;
|
||||
padding: 0 16px;
|
||||
.phone-header-black {
|
||||
height: 99px;
|
||||
border-radius: 20px 20px 0 0;
|
||||
background-size: 100%;
|
||||
.title {
|
||||
height: 99px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
padding-bottom: 13px;
|
||||
|
||||
.title-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.back {
|
||||
display: block;
|
||||
width: 50px;
|
||||
margin-left: 3px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.share {
|
||||
display: block;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ad-bar {
|
||||
background-color: var(--grey-10);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
.phone-body-bg {
|
||||
padding: 0 16px;
|
||||
height: 680px;
|
||||
border-radius: 0 0 44px 44px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.phone-body {
|
||||
width: 356px;
|
||||
margin-left: 17px;
|
||||
height: 630px;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 98px;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
.comments {
|
||||
padding: 20px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.form-data {
|
||||
padding: 10px 20px;
|
||||
.submit-btn {
|
||||
padding: 30px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.goods {
|
||||
.price {
|
||||
font-size: 18px;
|
||||
}
|
||||
.ele-cell-title {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
.goods-attr {
|
||||
}
|
||||
}
|
||||
.goods-divider {
|
||||
height: 6px;
|
||||
}
|
||||
.buy-bar {
|
||||
position: fixed;
|
||||
width: 356px;
|
||||
bottom: 70px;
|
||||
//top: 881px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0 0 44px 44px;
|
||||
border-top: 1px solid var(--grey-8);
|
||||
.ele-cell-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.home-btn,
|
||||
.shop-btn,
|
||||
.order-btn,
|
||||
.user-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 13px;
|
||||
padding: 0 9px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.icon {
|
||||
font-size: 19px;
|
||||
}
|
||||
.buy-btn {
|
||||
display: flex;
|
||||
.add-cart {
|
||||
border-radius: 100px 0 0 100px;
|
||||
border: none;
|
||||
background-color: var(--orange-5);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
.buy-now {
|
||||
border-radius: 0 100px 100px 0;
|
||||
border: none;
|
||||
background-color: var(--red-6);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.slick-slide) {
|
||||
overflow: hidden;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
font-size: 38px;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
color: #fff;
|
||||
background-color: rgba(31, 45, 61, 0.11);
|
||||
transition: ease all 0.3s;
|
||||
opacity: 0.3;
|
||||
z-index: 1;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:before) {
|
||||
display: none;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:hover) {
|
||||
color: #fff;
|
||||
opacity: 0.5;
|
||||
}
|
||||
:deep(.slick-slide h3) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
height: 170px;
|
||||
margin: 0 1px;
|
||||
background-color: var(--grey-10);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
.user-avatar {
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.user-info {
|
||||
margin-left: 10px;
|
||||
.nickname {
|
||||
color: var(--grey-3);
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.phone {
|
||||
color: var(--grey-5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-card {
|
||||
width: 330px;
|
||||
margin: 6px auto;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.merchant-card-title {
|
||||
width: 340px;
|
||||
margin: 6px auto;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.merchant-card {
|
||||
width: 340px;
|
||||
max-height: 80px;
|
||||
margin: 6px auto;
|
||||
margin-bottom: 16px;
|
||||
padding: 8px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
.merchant-name {
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
}
|
||||
.merchant-desc {
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.tools-card {
|
||||
width: 340px;
|
||||
margin: 0 1px;
|
||||
padding: 6px 16px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
position: absolute;
|
||||
top: 324px;
|
||||
left: 24px;
|
||||
.ele-cell {
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid var(--grey-9);
|
||||
}
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.order-card {
|
||||
width: 330px;
|
||||
height: 80px;
|
||||
margin: 0 auto;
|
||||
padding: 8px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
//position: absolute;
|
||||
//top: 230px;
|
||||
//left: 24px;
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,291 +0,0 @@
|
||||
<template>
|
||||
<a-page-header :title="title" @back="() => $router.go(-1)">
|
||||
<div class="ele-cell ele-cell-align-top">
|
||||
<!-- 设计画布 -->
|
||||
<div class="body ele-cell-content ele-bg-white" style="display: ">
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="menuId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
tool-class="ele-toolbar-form"
|
||||
class="sys-org-table"
|
||||
>
|
||||
<template #toolbar>
|
||||
<search
|
||||
@search="reload"
|
||||
:selection="selection"
|
||||
@add="openEdit"
|
||||
@remove="removeBatch"
|
||||
@batchMove="openMove"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="ele-text-secondary"
|
||||
>温馨提示:选图标可以上<a
|
||||
href="https://www.iconfont.cn"
|
||||
target="_blank"
|
||||
>阿里巴巴矢量图标库</a
|
||||
>,修改完后需要<a @click="clearSiteInfoCache">清除缓存</a
|
||||
>才会生效</div
|
||||
>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'icon'">
|
||||
<a-avatar :src="record.icon" :width="50" />
|
||||
</template>
|
||||
<template v-if="column.key === 'path'">
|
||||
<span class="ele-text-placeholder">{{ record.path }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'component'">
|
||||
<span class="ele-text-placeholder">{{ record.component }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 载入模拟器 -->
|
||||
<Simulator :form="form" :type="type" :refresh="refresh" @done="reload" />
|
||||
<!-- 中间间隙 -->
|
||||
<div style="width: 500px"></div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<MpMenuEdit
|
||||
v-model:visible="showEdit"
|
||||
:type="type"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</div>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref, unref, watch } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import Search from './components/search.vue';
|
||||
import MpMenuEdit from './components/mpMenuEdit.vue';
|
||||
import Simulator from './components/simulator.vue';
|
||||
import {
|
||||
pageMpMenu,
|
||||
removeMpMenu,
|
||||
removeBatchMpMenu
|
||||
} from '@/api/cms/mp-menu';
|
||||
import type { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
import { getPageTitle, openUrl } from '@/utils/common';
|
||||
import { removeSiteInfoCache } from '@/api/cms/website';
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格选中数据
|
||||
const selection = ref<MpMenu[]>([]);
|
||||
// 当前编辑数据
|
||||
const current = ref<MpMenu | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
// 加载状态
|
||||
const loading = ref(true);
|
||||
// 页面标题
|
||||
const title = getPageTitle();
|
||||
const type = ref<number>(2);
|
||||
const refresh = ref(false);
|
||||
// 模拟器的字段
|
||||
const form = ref<any>({
|
||||
pageName: '首页',
|
||||
showOrderCard: true,
|
||||
showCarousel: true,
|
||||
showMenuCard: true,
|
||||
showShopCard: true,
|
||||
showTtrainCard: false
|
||||
});
|
||||
// 菜单列表
|
||||
const list = ref<any[]>();
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({
|
||||
page,
|
||||
limit,
|
||||
where,
|
||||
orders,
|
||||
filters
|
||||
}) => {
|
||||
if (filters) {
|
||||
where.status = filters.status;
|
||||
}
|
||||
where.type = 2;
|
||||
return pageMpMenu({
|
||||
...where,
|
||||
...orders,
|
||||
page,
|
||||
limit
|
||||
});
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'menuId',
|
||||
key: 'menuId',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单图标',
|
||||
dataIndex: 'icon',
|
||||
key: 'icon',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '路由地址',
|
||||
dataIndex: 'path',
|
||||
key: 'path'
|
||||
},
|
||||
{
|
||||
title: '所在行',
|
||||
dataIndex: 'rows',
|
||||
key: 'rows',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sortNumber',
|
||||
key: 'sortNumber',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
hideInSetting: true
|
||||
}
|
||||
]);
|
||||
|
||||
/* 搜索 */
|
||||
const reload = (where?: MpMenuParam) => {
|
||||
type.value = Number(where?.type) || 0;
|
||||
refresh.value = !refresh.value;
|
||||
selection.value = [];
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
// 清除缓存
|
||||
const clearSiteInfoCache = () => {
|
||||
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId')).then(
|
||||
(msg) => {
|
||||
if (msg) {
|
||||
message.success('缓存已更新');
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: MpMenu) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeMpMenu(row.menuId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 批量删除 */
|
||||
const removeBatch = () => {
|
||||
if (!selection.value.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的记录吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchMpMenu(selection.value.map((d) => d.menuId))
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 自定义行属性 */
|
||||
const customRow = (record: MpMenu) => {
|
||||
return {
|
||||
// 行点击事件
|
||||
onClick: () => {
|
||||
// console.log(record);
|
||||
},
|
||||
// 行双击事件
|
||||
onDblclick: () => {
|
||||
openEdit(record);
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'MpMenuHome'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -1,261 +0,0 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '编辑菜单' : '添加菜单'"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="页面" name="type">
|
||||
<a-select
|
||||
v-model:value="form.type"
|
||||
placeholder="页面"
|
||||
:style="`width: 200px`"
|
||||
>
|
||||
<a-select-option :value="2">首页</a-select-option>
|
||||
<a-select-option :value="3">商城</a-select-option>
|
||||
<a-select-option :value="1">订单</a-select-option>
|
||||
<a-select-option :value="0">我的</a-select-option>
|
||||
<a-select-option :value="4">管理</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="路由地址" name="path">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入路由地址"
|
||||
v-model:value="form.path"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图标颜色" name="color">
|
||||
<ele-color-picker
|
||||
:show-alpha="true"
|
||||
v-model:value="form.color"
|
||||
:predefine="predefineColors"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject, uuid } from 'ele-admin-pro';
|
||||
import { addMpMenu, updateMpMenu } from '@/api/cms/mp-menu';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 类型 0服务 1订单
|
||||
type?: number;
|
||||
// 修改回显的数据
|
||||
data?: MpMenu | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否显示最大化切换按钮
|
||||
const maxable = ref(true);
|
||||
// 表格选中数据
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const images = ref<ItemType[]>([]);
|
||||
|
||||
// 用户信息
|
||||
const form = reactive<MpMenu>({
|
||||
menuId: undefined,
|
||||
parentId: 0,
|
||||
title: '',
|
||||
type: 0,
|
||||
isMpWeixin: true,
|
||||
path: undefined,
|
||||
component: undefined,
|
||||
target: undefined,
|
||||
icon: '',
|
||||
color: undefined,
|
||||
hide: undefined,
|
||||
position: undefined,
|
||||
active: undefined,
|
||||
userId: 0,
|
||||
home: undefined,
|
||||
sortNumber: 100,
|
||||
comments: '',
|
||||
status: 0
|
||||
});
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写菜单名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
path: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写路由地址',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const chooseFile = (data: FileRecord) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.thumbnail,
|
||||
status: 'done'
|
||||
});
|
||||
form.icon = data.thumbnail;
|
||||
};
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index, 1);
|
||||
form.icon = '';
|
||||
};
|
||||
|
||||
// 预设颜色
|
||||
const predefineColors = ref([
|
||||
'#40a9ff',
|
||||
'#9254de',
|
||||
'#36cfc9',
|
||||
'#73d13d',
|
||||
'#f759ab',
|
||||
'#cf1313',
|
||||
'#ff4d4f',
|
||||
'#ffa940',
|
||||
'#ffc53d',
|
||||
'#f3d3d3',
|
||||
'#1b1b1b',
|
||||
'#363636',
|
||||
'#4d4d4d',
|
||||
'#737373',
|
||||
'#a6a6a6',
|
||||
'#d9d9d9',
|
||||
'#e6e6e6',
|
||||
'#f2f2f2',
|
||||
'#f7f7f7',
|
||||
'#fafafa'
|
||||
]);
|
||||
|
||||
const { resetFields } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateMpMenu : addMpMenu;
|
||||
saveOrUpdate(formData)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
images.value = [];
|
||||
if (props.type) {
|
||||
form.type = props.type;
|
||||
}
|
||||
if (props.data) {
|
||||
assignObject(form, props.data);
|
||||
if (props.data.icon) {
|
||||
images.value.push({
|
||||
uid: uuid(),
|
||||
url: props.data.icon,
|
||||
status: 'done'
|
||||
});
|
||||
}
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,61 +0,0 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
<span>添加</span>
|
||||
</a-button>
|
||||
<a-space>
|
||||
<span class="ele-text-placeholder" style="margin-left: 20px"
|
||||
>菜单类型</span
|
||||
>
|
||||
<a-radio-group v-model:value="where.type" @change="handleSearch">
|
||||
<a-radio-button :value="1">订单图标</a-radio-button>
|
||||
<a-radio-button :value="0">功能图标</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { watch } from 'vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: MpMenuParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
(e: 'batchMove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where } = useSearch<MpMenu>({
|
||||
type: 0
|
||||
});
|
||||
|
||||
const handleSearch = () => {
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
@@ -1,284 +0,0 @@
|
||||
<template>
|
||||
<a-page-header :title="title" @back="() => $router.go(-1)">
|
||||
<div class="ele-cell ele-cell-align-top">
|
||||
<!-- 设计画布 -->
|
||||
<div class="body ele-cell-content ele-bg-white">
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="menuId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
tool-class="ele-toolbar-form"
|
||||
class="sys-org-table"
|
||||
>
|
||||
<template #toolbar>
|
||||
<search
|
||||
@search="reload"
|
||||
:selection="selection"
|
||||
@add="openEdit"
|
||||
@remove="removeBatch"
|
||||
@batchMove="openMove"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="ele-text-secondary"
|
||||
>温馨提示:选图标可以上<a
|
||||
href="https://www.iconfont.cn"
|
||||
target="_blank"
|
||||
>阿里巴巴矢量图标库</a
|
||||
>(https://www.iconfont.cn)</div
|
||||
>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'icon'">
|
||||
<a-avatar :src="record.icon" :width="50" />
|
||||
</template>
|
||||
<template v-if="column.key === 'path'">
|
||||
<span class="ele-text-placeholder">{{ record.path }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'component'">
|
||||
<span class="ele-text-placeholder">{{ record.component }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 中间间隙 -->
|
||||
<div style="width: 500px"></div>
|
||||
<!-- 载入模拟器 -->
|
||||
<Simulator :form="form" :type="type" />
|
||||
</div>
|
||||
</a-page-header>
|
||||
<div class="page">
|
||||
<div class="ele-body">
|
||||
<!-- 编辑弹窗 -->
|
||||
<MpMenuEdit
|
||||
v-model:visible="showEdit"
|
||||
:type="type"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import Search from './components/search.vue';
|
||||
import MpMenuEdit from './components/mpMenuEdit.vue';
|
||||
import {
|
||||
pageMpMenu,
|
||||
removeMpMenu,
|
||||
removeBatchMpMenu
|
||||
} from '@/api/cms/mp-menu';
|
||||
import type { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
import { getPageTitle } from '@/utils/common';
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格选中数据
|
||||
const selection = ref<MpMenu[]>([]);
|
||||
// 当前编辑数据
|
||||
const current = ref<MpMenu | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
// 加载状态
|
||||
const loading = ref(true);
|
||||
// 页面标题
|
||||
const title = getPageTitle();
|
||||
const type = ref<number>(0);
|
||||
// 模拟器的字段
|
||||
const form = ref<any>({
|
||||
pageName: '个人中心',
|
||||
showUserCard: true,
|
||||
showOrderCard: true,
|
||||
showToolsCard: true
|
||||
});
|
||||
// 菜单列表
|
||||
const list = ref<any[]>();
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({
|
||||
page,
|
||||
limit,
|
||||
where,
|
||||
orders,
|
||||
filters
|
||||
}) => {
|
||||
if (filters) {
|
||||
where.status = filters.status;
|
||||
}
|
||||
return pageMpMenu({
|
||||
...where,
|
||||
...orders,
|
||||
page,
|
||||
limit
|
||||
});
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'menuId',
|
||||
key: 'menuId',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单图标',
|
||||
dataIndex: 'icon',
|
||||
key: 'icon',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '路由地址',
|
||||
dataIndex: 'path',
|
||||
key: 'path',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '组件地址',
|
||||
dataIndex: 'component',
|
||||
key: 'component',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sortNumber',
|
||||
key: 'sortNumber',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
hideInSetting: true
|
||||
}
|
||||
]);
|
||||
|
||||
/* 搜索 */
|
||||
const reload = (where?: MpMenuParam) => {
|
||||
type.value = Number(where?.type) || 0;
|
||||
selection.value = [];
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: MpMenu) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeMpMenu(row.menuId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 批量删除 */
|
||||
const removeBatch = () => {
|
||||
if (!selection.value.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的记录吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchMpMenu(selection.value.map((d) => d.menuId))
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 查询 */
|
||||
const query = () => {
|
||||
loading.value = true;
|
||||
};
|
||||
|
||||
/* 自定义行属性 */
|
||||
const customRow = (record: MpMenu) => {
|
||||
return {
|
||||
// 行点击事件
|
||||
onClick: () => {
|
||||
// console.log(record);
|
||||
},
|
||||
// 行双击事件
|
||||
onDblclick: () => {
|
||||
openEdit(record);
|
||||
}
|
||||
};
|
||||
};
|
||||
query();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'MpMenuIndex'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -1,261 +0,0 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '编辑菜单' : '添加菜单'"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="页面" name="type">
|
||||
<a-select
|
||||
v-model:value="form.type"
|
||||
placeholder="页面"
|
||||
:style="`width: 200px`"
|
||||
>
|
||||
<a-select-option :value="2">首页</a-select-option>
|
||||
<a-select-option :value="3">商城</a-select-option>
|
||||
<a-select-option :value="1">订单</a-select-option>
|
||||
<a-select-option :value="0">我的</a-select-option>
|
||||
<a-select-option :value="4">管理</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="路由地址" name="path">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入路由地址"
|
||||
v-model:value="form.path"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图标颜色" name="color">
|
||||
<ele-color-picker
|
||||
:show-alpha="true"
|
||||
v-model:value="form.color"
|
||||
:predefine="predefineColors"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject, uuid } from 'ele-admin-pro';
|
||||
import { addMpMenu, updateMpMenu } from '@/api/cms/mp-menu';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 类型 0服务 1订单
|
||||
type?: number;
|
||||
// 修改回显的数据
|
||||
data?: MpMenu | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否显示最大化切换按钮
|
||||
const maxable = ref(true);
|
||||
// 表格选中数据
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const images = ref<ItemType[]>([]);
|
||||
|
||||
// 用户信息
|
||||
const form = reactive<MpMenu>({
|
||||
menuId: undefined,
|
||||
parentId: 0,
|
||||
title: '',
|
||||
type: 2,
|
||||
isMpWeixin: true,
|
||||
path: undefined,
|
||||
component: undefined,
|
||||
target: undefined,
|
||||
icon: '',
|
||||
color: undefined,
|
||||
hide: undefined,
|
||||
position: undefined,
|
||||
active: undefined,
|
||||
userId: 0,
|
||||
home: undefined,
|
||||
sortNumber: 100,
|
||||
comments: '',
|
||||
status: 0
|
||||
});
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写菜单名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
path: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写路由地址',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const chooseFile = (data: FileRecord) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.thumbnail,
|
||||
status: 'done'
|
||||
});
|
||||
form.icon = data.thumbnail;
|
||||
};
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index, 1);
|
||||
form.icon = '';
|
||||
};
|
||||
|
||||
// 预设颜色
|
||||
const predefineColors = ref([
|
||||
'#40a9ff',
|
||||
'#9254de',
|
||||
'#36cfc9',
|
||||
'#73d13d',
|
||||
'#f759ab',
|
||||
'#cf1313',
|
||||
'#ff4d4f',
|
||||
'#ffa940',
|
||||
'#ffc53d',
|
||||
'#f3d3d3',
|
||||
'#1b1b1b',
|
||||
'#363636',
|
||||
'#4d4d4d',
|
||||
'#737373',
|
||||
'#a6a6a6',
|
||||
'#d9d9d9',
|
||||
'#e6e6e6',
|
||||
'#f2f2f2',
|
||||
'#f7f7f7',
|
||||
'#fafafa'
|
||||
]);
|
||||
|
||||
const { resetFields } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateMpMenu : addMpMenu;
|
||||
saveOrUpdate(formData)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
images.value = [];
|
||||
if (props.type) {
|
||||
form.type = props.type;
|
||||
}
|
||||
if (props.data) {
|
||||
assignObject(form, props.data);
|
||||
if (props.data.icon) {
|
||||
images.value.push({
|
||||
uid: uuid(),
|
||||
url: props.data.icon,
|
||||
status: 'done'
|
||||
});
|
||||
}
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,52 +0,0 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<!-- <a-button type="primary" class="ele-btn-icon" @click="add">-->
|
||||
<!-- <template #icon>-->
|
||||
<!-- <PlusOutlined />-->
|
||||
<!-- </template>-->
|
||||
<!-- <span>添加</span>-->
|
||||
<!-- </a-button>-->
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { watch } from 'vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: MpMenuParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
(e: 'batchMove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where } = useSearch<MpMenu>({
|
||||
type: 2
|
||||
});
|
||||
|
||||
const handleSearch = () => {
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
@@ -1,533 +0,0 @@
|
||||
<template>
|
||||
<div class="phone-layout" v-if="form">
|
||||
<div class="phone-header-black ele-fluid">
|
||||
<div class="title ele-fluid">
|
||||
<div class="title-bar">
|
||||
<span class="back"></span>
|
||||
<span>{{ form.pageName || '订单' }}</span>
|
||||
<a class="share" @click="onShare"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="form.showOrderCard">
|
||||
<div class="phone-body" style="overflow-y: auto; overflow-x: hidden">
|
||||
<div class="tabs-card">
|
||||
<a-tabs v-model:activeKey="activeKey">
|
||||
<a-tab-pane key="1" tab="全部订单" />
|
||||
<a-tab-pane key="2" tab="未支付" force-render />
|
||||
<a-tab-pane key="3" tab="已支付" />
|
||||
<a-tab-pane key="4" tab="已完成" />
|
||||
<a-tab-pane key="5" tab="押金" />
|
||||
</a-tabs>
|
||||
</div>
|
||||
<!-- 订单列表 -->
|
||||
<template v-for="(item, index) in orderList" :key="index">
|
||||
<a-card
|
||||
size="small"
|
||||
class="order-item"
|
||||
:title="item.merchantName"
|
||||
:body-style="{ padding: '8px' }"
|
||||
>
|
||||
<template #title>
|
||||
{{ item.name }}
|
||||
</template>
|
||||
<template #extra
|
||||
><span class="ele-text-success">已支付</span></template
|
||||
>
|
||||
<template #actions>
|
||||
<div class="ele-cell card-actions">
|
||||
<div class="card-actions-item" @click="actionsClick">
|
||||
<a-button class="mini-btn" size="mini">申请开票</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="order-body ele-cell ele-cell-align-top">
|
||||
<!-- <uv-avatar :src="item.merchantAvatar" shape="square" size="90"></uv-avatar> -->
|
||||
<div class="ele-cell-content">
|
||||
<block
|
||||
v-for="(sub, subIndex) in item.orderInfoList"
|
||||
:key="subIndex"
|
||||
>
|
||||
<div class="ele-cell">
|
||||
<div class="ele-text-secondary">时间:</div>
|
||||
<div class="ele-text-heading">{{ sub.dateTime }}</div>
|
||||
</div>
|
||||
<div class="ele-cell">
|
||||
<div class="ele-text-secondary">场地:</div>
|
||||
<div class="ele-text-heading">{{ sub.fieldName }}</div>
|
||||
</div>
|
||||
<div class="ele-cell">
|
||||
<div class="ele-text-secondary">金额:</div>
|
||||
<div class="ele-text-heading">¥{{ sub.price }}</div>
|
||||
</div>
|
||||
<div style="padding-bottom: 8px"></div>
|
||||
</block>
|
||||
<div class="ele-cell">
|
||||
<div class="ele-text-placeholder" style="flex: 1;"
|
||||
>共2件</div
|
||||
>
|
||||
<div class="ele-cell" style="font-weight: bold">
|
||||
<div class="ele-text-heading"
|
||||
>实付:</div
|
||||
>
|
||||
<div class="ele-text-heading">¥{{ item.payPrice }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<a-card
|
||||
class="buy-bar"
|
||||
:bordered="false"
|
||||
:body-style="{ padding: '12px 16px' }"
|
||||
>
|
||||
<div class="ele-cell">
|
||||
<a
|
||||
class="home-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/home`)"
|
||||
>
|
||||
<HomeOutlined class="icon" />
|
||||
<span>首页</span>
|
||||
</a>
|
||||
<a
|
||||
class="shop-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/shop`)"
|
||||
>
|
||||
<ShopOutlined class="icon" />
|
||||
<span>商城</span>
|
||||
</a>
|
||||
<a
|
||||
class="order-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/order`)"
|
||||
>
|
||||
<ProfileOutlined class="icon ele-text-danger" />
|
||||
<span class="ele-text-danger">订单</span>
|
||||
</a>
|
||||
<a
|
||||
class="user-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/user`)"
|
||||
>
|
||||
<UserOutlined class="icon" />
|
||||
我的
|
||||
</a>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ProfileOutlined,
|
||||
ShopOutlined,
|
||||
HomeOutlined,
|
||||
UserOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { MpWeixinParam, WebsiteField } from '@/api/cms/website/field/model';
|
||||
import { listWebsiteField } from '@/api/system/website/field';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { listMpMenu } from '@/api/cms/mp-menu';
|
||||
import { listAd } from '@/api/cms/ad';
|
||||
import { listMerchant } from '@/api/shop/merchant';
|
||||
import { Merchant } from '@/api/shop/merchant/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { useRouter } from 'vue-router';
|
||||
const { currentRoute } = useRouter();
|
||||
const { query } = unref(currentRoute);
|
||||
|
||||
import MpMenuEdit from '@/views/cms/mpDesign/components/mpMenuEdit.vue';
|
||||
import { listOrder, pageOrder } from '@/api/shop/order';
|
||||
import { Order } from '@/api/shop/order/model';
|
||||
|
||||
const prpos = withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
form?: any | null;
|
||||
type?: number;
|
||||
list?: any[] | null;
|
||||
refresh?: boolean;
|
||||
}>(),
|
||||
{
|
||||
placeholder: undefined
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const param = ref<MpWeixinParam>({});
|
||||
const showUserCardEdit = ref(false);
|
||||
const showMpMenuEdit = ref(false);
|
||||
// 当前编辑数据
|
||||
const current = ref<WebsiteField | null>(null);
|
||||
|
||||
// 幻灯片广告
|
||||
const adImageList = ref<any[]>();
|
||||
// 首页导航图标
|
||||
const scrollList = ref<any[]>();
|
||||
// 订单图标
|
||||
const order = ref<any[]>();
|
||||
// 服务图标
|
||||
const server = ref<any[]>();
|
||||
// 商户列表
|
||||
const shopList = ref<Merchant[]>();
|
||||
// 订单列表
|
||||
const orderList = ref<Order[]>();
|
||||
|
||||
const config = ref({
|
||||
selector: '#content', //容器,可使用css选择器
|
||||
branding: false,
|
||||
language: 'zh_CN', //调用放在langs文件夹内的语言包
|
||||
toolbar: false, //隐藏工具栏
|
||||
menubar: false, //隐藏菜单栏
|
||||
inline: true, //开启内联模式
|
||||
plugins: [] //选择需加载的插件
|
||||
//选中时出现的快捷工具,与插件有依赖关系
|
||||
// quickbars_selection_toolbar: 'bold italic forecolor | link blockquote quickimage',
|
||||
// init_instance_callback: function (editor) {
|
||||
// editor.setContent('这里是你的内容字符串');
|
||||
// }
|
||||
});
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openUserCard = (row?: WebsiteField) => {
|
||||
current.value = row ?? null;
|
||||
showUserCardEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openMpMenuEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showMpMenuEdit.value = true;
|
||||
};
|
||||
|
||||
const onShare = () => {};
|
||||
|
||||
const activeKey = ref('1');
|
||||
|
||||
const reload = () => {
|
||||
// listWebsiteField({}).then((list) => {
|
||||
// list.map((d) => {
|
||||
// const key = String(d.name);
|
||||
// param.value[key] = d.value;
|
||||
// });
|
||||
// });
|
||||
|
||||
// listMpMenu({}).then((list) => {
|
||||
// server.value = list.filter((d) => d.type == 0);
|
||||
// order.value = list.filter((d) => d.type == 1);
|
||||
// scrollList.value = list.filter((d) => d.type == 2);
|
||||
// });
|
||||
//
|
||||
// listAd({ adType: '幻灯片' }).then((res) => {
|
||||
// const carouselImages = res[0].images;
|
||||
// if (carouselImages) {
|
||||
// adImageList.value = JSON.parse(carouselImages);
|
||||
// }
|
||||
// });
|
||||
|
||||
// listMerchant({}).then((list) => {
|
||||
// shopList.value = list;
|
||||
// });
|
||||
|
||||
pageOrder({ userId: 13448 }).then((res) => {
|
||||
orderList.value = res?.list;
|
||||
});
|
||||
emit('done');
|
||||
};
|
||||
|
||||
const actionsClick = () => {};
|
||||
|
||||
reload();
|
||||
|
||||
watch(
|
||||
() => prpos.refresh,
|
||||
(refresh) => {
|
||||
if (refresh) {
|
||||
reload();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.phone-layout {
|
||||
position: fixed;
|
||||
right: 26px;
|
||||
width: 390px;
|
||||
height: 844px;
|
||||
background: url('@/assets/img/app-ui.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: top;
|
||||
background-size: 100%;
|
||||
//position: relative;
|
||||
padding: 0 16px;
|
||||
.phone-header-black {
|
||||
height: 99px;
|
||||
border-radius: 20px 20px 0 0;
|
||||
background-size: 100%;
|
||||
.title {
|
||||
height: 99px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
padding-bottom: 13px;
|
||||
|
||||
.title-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.back {
|
||||
display: block;
|
||||
width: 50px;
|
||||
margin-left: 3px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.share {
|
||||
display: block;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.phone-body-bg {
|
||||
padding: 0 16px;
|
||||
height: 680px;
|
||||
border-radius: 0 0 44px 44px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.phone-body {
|
||||
width: 356px;
|
||||
margin-left: 17px;
|
||||
height: 630px;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 98px;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
.comments {
|
||||
padding: 20px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.form-data {
|
||||
padding: 10px 20px;
|
||||
.submit-btn {
|
||||
padding: 30px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.goods {
|
||||
.price {
|
||||
font-size: 18px;
|
||||
}
|
||||
.ele-cell-title {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
.goods-attr {
|
||||
}
|
||||
}
|
||||
.goods-divider {
|
||||
height: 6px;
|
||||
}
|
||||
.buy-bar {
|
||||
position: fixed;
|
||||
width: 356px;
|
||||
bottom: 70px;
|
||||
//top: 881px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0 0 44px 44px;
|
||||
border-top: 1px solid var(--grey-8);
|
||||
.ele-cell-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.home-btn,
|
||||
.shop-btn,
|
||||
.order-btn,
|
||||
.user-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 13px;
|
||||
padding: 0 9px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.icon {
|
||||
font-size: 19px;
|
||||
}
|
||||
.buy-btn {
|
||||
display: flex;
|
||||
.add-cart {
|
||||
border-radius: 100px 0 0 100px;
|
||||
border: none;
|
||||
background-color: var(--orange-5);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
.buy-now {
|
||||
border-radius: 0 100px 100px 0;
|
||||
border: none;
|
||||
background-color: var(--red-6);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.slick-slide) {
|
||||
overflow: hidden;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
font-size: 38px;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
color: #fff;
|
||||
background-color: rgba(31, 45, 61, 0.11);
|
||||
transition: ease all 0.3s;
|
||||
opacity: 0.3;
|
||||
z-index: 1;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:before) {
|
||||
display: none;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:hover) {
|
||||
color: #fff;
|
||||
opacity: 0.5;
|
||||
}
|
||||
:deep(.slick-slide h3) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
height: 170px;
|
||||
margin: 0 1px;
|
||||
background-color: var(--grey-10);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
.user-avatar {
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.user-info {
|
||||
margin-left: 10px;
|
||||
.nickname {
|
||||
color: var(--grey-3);
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.phone {
|
||||
color: var(--grey-5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-card {
|
||||
width: 340px;
|
||||
height: 80px;
|
||||
margin: 6px auto;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.merchant-card-title {
|
||||
width: 340px;
|
||||
margin: 6px auto;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.merchant-card {
|
||||
width: 340px;
|
||||
max-height: 80px;
|
||||
margin: 6px auto;
|
||||
margin-bottom: 16px;
|
||||
padding: 8px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
.merchant-name {
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
}
|
||||
.merchant-desc {
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.tools-card {
|
||||
width: 340px;
|
||||
margin: 0 1px;
|
||||
padding: 6px 16px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
position: absolute;
|
||||
top: 324px;
|
||||
left: 24px;
|
||||
.ele-cell {
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid var(--grey-9);
|
||||
}
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-card {
|
||||
padding: 0 8px;
|
||||
//.order-item {
|
||||
// background-color: #ffffff;
|
||||
// padding: 10px;
|
||||
// min-height: 200px;
|
||||
//}
|
||||
}
|
||||
|
||||
.order-item {
|
||||
margin-bottom: 10px;
|
||||
.order-title {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1px #eee solid;
|
||||
|
||||
.merchant-name {
|
||||
padding-left: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.order-body {
|
||||
}
|
||||
.card-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,288 +0,0 @@
|
||||
<template>
|
||||
<a-page-header :title="title" @back="() => $router.go(-1)">
|
||||
<div class="ele-cell ele-cell-align-top">
|
||||
<!-- 设计画布 -->
|
||||
<div class="body ele-cell-content ele-bg-white" style="display: ">
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="menuId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
tool-class="ele-toolbar-form"
|
||||
class="sys-org-table"
|
||||
>
|
||||
<template #toolbar>
|
||||
<search
|
||||
@search="reload"
|
||||
:selection="selection"
|
||||
@add="openEdit"
|
||||
@remove="removeBatch"
|
||||
@batchMove="openMove"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="ele-text-secondary"
|
||||
>温馨提示:选图标可以上<a
|
||||
href="https://www.iconfont.cn"
|
||||
target="_blank"
|
||||
>阿里巴巴矢量图标库</a
|
||||
>,修改完后需要<a @click="clearSiteInfoCache"
|
||||
>清除缓存</a
|
||||
>才会生效</div
|
||||
>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'icon'">
|
||||
<a-avatar :src="record.icon" :width="50" />
|
||||
</template>
|
||||
<template v-if="column.key === 'path'">
|
||||
<span class="ele-text-placeholder">{{ record.path }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'component'">
|
||||
<span class="ele-text-placeholder">{{ record.component }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 载入模拟器 -->
|
||||
<Simulator :form="form" :type="type" :refresh="refresh" @done="reload" />
|
||||
<!-- 中间间隙 -->
|
||||
<div style="width: 500px"></div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<MpMenuEdit
|
||||
v-model:visible="showEdit"
|
||||
:type="type"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</div>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref, unref, watch } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import Search from './components/search.vue';
|
||||
import MpMenuEdit from './components/mpMenuEdit.vue';
|
||||
import Simulator from './components/simulator.vue';
|
||||
import {
|
||||
pageMpMenu,
|
||||
removeMpMenu,
|
||||
removeBatchMpMenu
|
||||
} from '@/api/cms/mp-menu';
|
||||
import type { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
import { getPageTitle, openUrl } from '@/utils/common';
|
||||
import { removeSiteInfoCache } from "@/api/cms/website";
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格选中数据
|
||||
const selection = ref<MpMenu[]>([]);
|
||||
// 当前编辑数据
|
||||
const current = ref<MpMenu | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
// 加载状态
|
||||
const loading = ref(true);
|
||||
// 页面标题
|
||||
const title = getPageTitle();
|
||||
const type = ref<number>(2);
|
||||
const refresh = ref(false);
|
||||
// 模拟器的字段
|
||||
const form = ref<any>({
|
||||
pageName: '场馆预定订单',
|
||||
showOrderCard: true
|
||||
});
|
||||
// 菜单列表
|
||||
const list = ref<any[]>();
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({
|
||||
page,
|
||||
limit,
|
||||
where,
|
||||
orders,
|
||||
filters
|
||||
}) => {
|
||||
if (filters) {
|
||||
where.status = filters.status;
|
||||
}
|
||||
where.type = 5;
|
||||
return pageMpMenu({
|
||||
...where,
|
||||
...orders,
|
||||
page,
|
||||
limit
|
||||
});
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'menuId',
|
||||
key: 'menuId',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单图标',
|
||||
dataIndex: 'icon',
|
||||
key: 'icon',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '路由地址',
|
||||
dataIndex: 'path',
|
||||
key: 'path'
|
||||
},
|
||||
{
|
||||
title: '分类ID',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sortNumber',
|
||||
key: 'sortNumber',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
hideInSetting: true
|
||||
}
|
||||
]);
|
||||
|
||||
/* 搜索 */
|
||||
const reload = (where?: MpMenuParam) => {
|
||||
type.value = Number(where?.type) || 0;
|
||||
refresh.value = !refresh.value;
|
||||
selection.value = [];
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
// 清除缓存
|
||||
const clearSiteInfoCache = () => {
|
||||
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId')).then(
|
||||
(msg) => {
|
||||
if (msg) {
|
||||
message.success('缓存已更新');
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: MpMenu) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeMpMenu(row.menuId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 批量删除 */
|
||||
const removeBatch = () => {
|
||||
if (!selection.value.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的记录吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchMpMenu(selection.value.map((d) => d.menuId))
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 自定义行属性 */
|
||||
const customRow = (record: MpMenu) => {
|
||||
return {
|
||||
// 行点击事件
|
||||
onClick: () => {
|
||||
// console.log(record);
|
||||
},
|
||||
// 行双击事件
|
||||
onDblclick: () => {
|
||||
openEdit(record);
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'MpMenuOrder'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -1,261 +0,0 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '编辑菜单' : '添加菜单'"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="页面" name="type">
|
||||
<a-select
|
||||
v-model:value="form.type"
|
||||
placeholder="页面"
|
||||
:style="`width: 200px`"
|
||||
>
|
||||
<a-select-option :value="2">首页</a-select-option>
|
||||
<a-select-option :value="3">商城</a-select-option>
|
||||
<a-select-option :value="1">订单</a-select-option>
|
||||
<a-select-option :value="0">我的</a-select-option>
|
||||
<a-select-option :value="4">管理</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="路由地址" name="path">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入路由地址"
|
||||
v-model:value="form.path"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图标颜色" name="color">
|
||||
<ele-color-picker
|
||||
:show-alpha="true"
|
||||
v-model:value="form.color"
|
||||
:predefine="predefineColors"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject, uuid } from 'ele-admin-pro';
|
||||
import { addMpMenu, updateMpMenu } from '@/api/cms/mp-menu';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 类型 0服务 1订单
|
||||
type?: number;
|
||||
// 修改回显的数据
|
||||
data?: MpMenu | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否显示最大化切换按钮
|
||||
const maxable = ref(true);
|
||||
// 表格选中数据
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const images = ref<ItemType[]>([]);
|
||||
|
||||
// 用户信息
|
||||
const form = reactive<MpMenu>({
|
||||
menuId: undefined,
|
||||
parentId: 0,
|
||||
title: '',
|
||||
type: 2,
|
||||
isMpWeixin: true,
|
||||
path: undefined,
|
||||
component: undefined,
|
||||
target: undefined,
|
||||
icon: '',
|
||||
color: undefined,
|
||||
hide: undefined,
|
||||
position: undefined,
|
||||
active: undefined,
|
||||
userId: 0,
|
||||
home: undefined,
|
||||
sortNumber: 100,
|
||||
comments: '',
|
||||
status: 0
|
||||
});
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写菜单名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
path: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写路由地址',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const chooseFile = (data: FileRecord) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.thumbnail,
|
||||
status: 'done'
|
||||
});
|
||||
form.icon = data.thumbnail;
|
||||
};
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index, 1);
|
||||
form.icon = '';
|
||||
};
|
||||
|
||||
// 预设颜色
|
||||
const predefineColors = ref([
|
||||
'#40a9ff',
|
||||
'#9254de',
|
||||
'#36cfc9',
|
||||
'#73d13d',
|
||||
'#f759ab',
|
||||
'#cf1313',
|
||||
'#ff4d4f',
|
||||
'#ffa940',
|
||||
'#ffc53d',
|
||||
'#f3d3d3',
|
||||
'#1b1b1b',
|
||||
'#363636',
|
||||
'#4d4d4d',
|
||||
'#737373',
|
||||
'#a6a6a6',
|
||||
'#d9d9d9',
|
||||
'#e6e6e6',
|
||||
'#f2f2f2',
|
||||
'#f7f7f7',
|
||||
'#fafafa'
|
||||
]);
|
||||
|
||||
const { resetFields } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateMpMenu : addMpMenu;
|
||||
saveOrUpdate(formData)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
images.value = [];
|
||||
if (props.type) {
|
||||
form.type = props.type;
|
||||
}
|
||||
if (props.data) {
|
||||
assignObject(form, props.data);
|
||||
if (props.data.icon) {
|
||||
images.value.push({
|
||||
uid: uuid(),
|
||||
url: props.data.icon,
|
||||
status: 'done'
|
||||
});
|
||||
}
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,52 +0,0 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
<span>添加</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { watch } from 'vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: MpMenuParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
(e: 'batchMove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where } = useSearch<MpMenu>({
|
||||
type: 2
|
||||
});
|
||||
|
||||
const handleSearch = () => {
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
@@ -1,480 +0,0 @@
|
||||
<template>
|
||||
<div class="phone-layout" v-if="form">
|
||||
<div class="phone-header-black ele-fluid">
|
||||
<div class="title ele-fluid">
|
||||
<div class="title-bar">
|
||||
<span class="back"></span>
|
||||
<span>{{ form.pageName || '商城' }}</span>
|
||||
<a class="share" @click="onShare"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="phone-body" style="overflow-y: auto; overflow-x: hidden">
|
||||
<!-- 导航菜单 -->
|
||||
<template v-if="form.showMenuCard">
|
||||
<div class="menu-card ele-cell" style="margin-top: 10px">
|
||||
<div
|
||||
v-for="(item, index) in shopIcon"
|
||||
:key="index"
|
||||
class="ele-cell-content ele-text-center btn-center"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-image :src="item.icon" :width="30" :preview="false" />
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<MpMenuEdit
|
||||
v-model:visible="showMpMenuEdit"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
<!-- 商品列表 -->
|
||||
<template v-if="form.showGoodsCard">
|
||||
<div class="goods-card-title">商品列表</div>
|
||||
<div
|
||||
class="goods-card ele-cell"
|
||||
v-for="(item, index) in goodsList"
|
||||
:key="index"
|
||||
>
|
||||
<a-image
|
||||
:src="item.image ? JSON.parse(item.image)[0] : []"
|
||||
:width="60"
|
||||
:preview="false"
|
||||
/>
|
||||
<div class="goods-info ele-cell-content">
|
||||
<div class="goods-name">{{ item.title }}</div>
|
||||
<div class="goods-price ele-text-danger ele-cell">
|
||||
<div class="ele-cell-content">¥{{ item.price }}</div>
|
||||
<a-button size="mini">去购买</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<a-card
|
||||
class="buy-bar"
|
||||
:bordered="false"
|
||||
:body-style="{ padding: '12px 16px' }"
|
||||
>
|
||||
<div class="ele-cell">
|
||||
<a
|
||||
class="home-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/home`)"
|
||||
>
|
||||
<HomeOutlined class="icon" />
|
||||
<span>首页</span>
|
||||
</a>
|
||||
<a
|
||||
class="shop-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/shop`)"
|
||||
>
|
||||
<ShopOutlined class="icon ele-text-danger" />
|
||||
<span class="ele-text-danger">商城</span>
|
||||
</a>
|
||||
<a
|
||||
class="order-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/order`)"
|
||||
>
|
||||
<ProfileOutlined class="icon" />
|
||||
订单
|
||||
</a>
|
||||
<a
|
||||
class="user-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/user`)"
|
||||
>
|
||||
<UserOutlined class="icon" />
|
||||
我的
|
||||
</a>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ProfileOutlined,
|
||||
ShopOutlined,
|
||||
HomeOutlined,
|
||||
UserOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { MpWeixinParam, WebsiteField } from '@/api/cms/website/field/model';
|
||||
import { listWebsiteField } from '@/api/system/website/field';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { listMpMenu } from '@/api/cms/mp-menu';
|
||||
import { listAd } from '@/api/cms/ad';
|
||||
import { listMerchant } from '@/api/shop/merchant';
|
||||
import { Merchant } from '@/api/shop/merchant/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { useRouter } from 'vue-router';
|
||||
const { currentRoute } = useRouter();
|
||||
const { query } = unref(currentRoute);
|
||||
|
||||
import MpMenuEdit from '@/views/cms/mpDesign/components/mpMenuEdit.vue';
|
||||
import { pageGoods } from '@/api/shop/goods';
|
||||
import { Goods } from '@/api/shop/goods/model';
|
||||
|
||||
const prpos = withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
form?: any | null;
|
||||
type?: number;
|
||||
list?: any[] | null;
|
||||
refresh?: boolean;
|
||||
}>(),
|
||||
{
|
||||
placeholder: undefined
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const param = ref<MpWeixinParam>({});
|
||||
const showUserCardEdit = ref(false);
|
||||
const showMpMenuEdit = ref(false);
|
||||
// 当前编辑数据
|
||||
const current = ref<WebsiteField | null>(null);
|
||||
|
||||
// 幻灯片广告
|
||||
const adImageList = ref<any[]>();
|
||||
// 首页导航图标
|
||||
const scrollList = ref<any[]>();
|
||||
// 订单图标
|
||||
const order = ref<any[]>();
|
||||
// 服务图标
|
||||
const server = ref<any[]>();
|
||||
// 商城导航图标
|
||||
const shopIcon = ref<any[]>();
|
||||
// 商户列表
|
||||
const shopList = ref<Merchant[]>();
|
||||
// 商品列表
|
||||
const goodsList = ref<Goods[]>();
|
||||
|
||||
const config = ref({
|
||||
selector: '#content', //容器,可使用css选择器
|
||||
branding: false,
|
||||
language: 'zh_CN', //调用放在langs文件夹内的语言包
|
||||
toolbar: false, //隐藏工具栏
|
||||
menubar: false, //隐藏菜单栏
|
||||
inline: true, //开启内联模式
|
||||
plugins: [] //选择需加载的插件
|
||||
//选中时出现的快捷工具,与插件有依赖关系
|
||||
// quickbars_selection_toolbar: 'bold italic forecolor | link blockquote quickimage',
|
||||
// init_instance_callback: function (editor) {
|
||||
// editor.setContent('这里是你的内容字符串');
|
||||
// }
|
||||
});
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openUserCard = (row?: WebsiteField) => {
|
||||
current.value = row ?? null;
|
||||
showUserCardEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openMpMenuEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showMpMenuEdit.value = true;
|
||||
};
|
||||
|
||||
const onShare = () => {};
|
||||
|
||||
const reload = () => {
|
||||
listWebsiteField({}).then((list) => {
|
||||
list.map((d) => {
|
||||
const key = String(d.name);
|
||||
param.value[key] = d.value;
|
||||
});
|
||||
});
|
||||
|
||||
listMpMenu({}).then((list) => {
|
||||
server.value = list.filter((d) => d.type == 0);
|
||||
order.value = list.filter((d) => d.type == 1);
|
||||
scrollList.value = list.filter((d) => d.type == 2);
|
||||
shopIcon.value = list.filter((d) => d.type == 3);
|
||||
});
|
||||
|
||||
listAd({ adType: '幻灯片' }).then((res) => {
|
||||
const carouselImages = res[0].images;
|
||||
if (carouselImages) {
|
||||
adImageList.value = JSON.parse(carouselImages);
|
||||
}
|
||||
});
|
||||
|
||||
listMerchant({}).then((list) => {
|
||||
shopList.value = list;
|
||||
});
|
||||
|
||||
pageGoods({}).then((res) => {
|
||||
goodsList.value = res?.list;
|
||||
});
|
||||
emit('done');
|
||||
};
|
||||
|
||||
reload();
|
||||
|
||||
watch(
|
||||
() => prpos.refresh,
|
||||
(refresh) => {
|
||||
if (refresh) {
|
||||
reload();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.phone-layout {
|
||||
position: fixed;
|
||||
right: 26px;
|
||||
width: 390px;
|
||||
height: 844px;
|
||||
background: url('@/assets/img/app-ui.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: top;
|
||||
background-size: 100%;
|
||||
//position: relative;
|
||||
padding: 0 16px;
|
||||
.phone-header-black {
|
||||
height: 99px;
|
||||
border-radius: 20px 20px 0 0;
|
||||
background-size: 100%;
|
||||
.title {
|
||||
height: 99px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
padding-bottom: 13px;
|
||||
|
||||
.title-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.back {
|
||||
display: block;
|
||||
width: 50px;
|
||||
margin-left: 3px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.share {
|
||||
display: block;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.phone-body-bg {
|
||||
padding: 0 16px;
|
||||
height: 680px;
|
||||
border-radius: 0 0 44px 44px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.phone-body {
|
||||
width: 356px;
|
||||
margin-left: 17px;
|
||||
height: 630px;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 98px;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
.comments {
|
||||
padding: 20px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.form-data {
|
||||
padding: 10px 20px;
|
||||
.submit-btn {
|
||||
padding: 30px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.goods {
|
||||
.price {
|
||||
font-size: 18px;
|
||||
}
|
||||
.ele-cell-title {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
.goods-attr {
|
||||
}
|
||||
}
|
||||
.goods-divider {
|
||||
height: 6px;
|
||||
}
|
||||
.buy-bar {
|
||||
position: fixed;
|
||||
width: 356px;
|
||||
bottom: 70px;
|
||||
//top: 881px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0 0 44px 44px;
|
||||
border-top: 1px solid var(--grey-8);
|
||||
.ele-cell-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.home-btn,
|
||||
.shop-btn,
|
||||
.order-btn,
|
||||
.user-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 13px;
|
||||
padding: 0 9px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.icon {
|
||||
font-size: 19px;
|
||||
}
|
||||
.buy-btn {
|
||||
display: flex;
|
||||
.add-cart {
|
||||
border-radius: 100px 0 0 100px;
|
||||
border: none;
|
||||
background-color: var(--orange-5);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
.buy-now {
|
||||
border-radius: 0 100px 100px 0;
|
||||
border: none;
|
||||
background-color: var(--red-6);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.slick-slide) {
|
||||
overflow: hidden;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
font-size: 38px;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
color: #fff;
|
||||
background-color: rgba(31, 45, 61, 0.11);
|
||||
transition: ease all 0.3s;
|
||||
opacity: 0.3;
|
||||
z-index: 1;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:before) {
|
||||
display: none;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:hover) {
|
||||
color: #fff;
|
||||
opacity: 0.5;
|
||||
}
|
||||
:deep(.slick-slide h3) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
height: 170px;
|
||||
margin: 0 1px;
|
||||
background-color: var(--grey-10);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
.user-avatar {
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.user-info {
|
||||
margin-left: 10px;
|
||||
.nickname {
|
||||
color: var(--grey-3);
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.phone {
|
||||
color: var(--grey-5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-card {
|
||||
width: 340px;
|
||||
height: 80px;
|
||||
margin: 6px auto;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.goods-card-title {
|
||||
width: 340px;
|
||||
margin: 6px auto;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.goods-card {
|
||||
width: 340px;
|
||||
max-height: 80px;
|
||||
margin: 6px auto;
|
||||
margin-bottom: 16px;
|
||||
padding: 8px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
.goods-name {
|
||||
font-weight: 500;
|
||||
font-size: 15px;
|
||||
}
|
||||
.goods-desc {
|
||||
width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.goods-price {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.tools-card {
|
||||
width: 340px;
|
||||
margin: 0 1px;
|
||||
padding: 6px 16px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
position: absolute;
|
||||
top: 324px;
|
||||
left: 24px;
|
||||
.ele-cell {
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid var(--grey-9);
|
||||
}
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,290 +0,0 @@
|
||||
<template>
|
||||
<a-page-header :title="title" @back="() => $router.go(-1)">
|
||||
<div class="ele-cell ele-cell-align-top">
|
||||
<!-- 设计画布 -->
|
||||
<div class="body ele-cell-content ele-bg-white" style="display: ">
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="menuId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
tool-class="ele-toolbar-form"
|
||||
class="sys-org-table"
|
||||
>
|
||||
<template #toolbar>
|
||||
<search
|
||||
@search="reload"
|
||||
:selection="selection"
|
||||
@add="openEdit"
|
||||
@remove="removeBatch"
|
||||
@batchMove="openMove"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="ele-text-secondary"
|
||||
>温馨提示:选图标可以上<a
|
||||
href="https://www.iconfont.cn"
|
||||
target="_blank"
|
||||
>阿里巴巴矢量图标库</a
|
||||
>,修改完后需要<a @click="clearSiteInfoCache"
|
||||
>清除缓存</a
|
||||
>才会生效</div
|
||||
>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'icon'">
|
||||
<a-avatar :src="record.icon" :width="50" />
|
||||
</template>
|
||||
<template v-if="column.key === 'path'">
|
||||
<span class="ele-text-placeholder">{{ record.path }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'component'">
|
||||
<span class="ele-text-placeholder">{{ record.component }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 载入模拟器 -->
|
||||
<Simulator :form="form" :type="type" :refresh="refresh" @done="reload" />
|
||||
<!-- 中间间隙 -->
|
||||
<div style="width: 500px"></div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<MpMenuEdit
|
||||
v-model:visible="showEdit"
|
||||
:type="type"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</div>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref, unref, watch } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import Search from './components/search.vue';
|
||||
import MpMenuEdit from './components/mpMenuEdit.vue';
|
||||
import Simulator from './components/simulator.vue';
|
||||
import {
|
||||
pageMpMenu,
|
||||
removeMpMenu,
|
||||
removeBatchMpMenu
|
||||
} from '@/api/cms/mp-menu';
|
||||
import type { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
import { getPageTitle, openUrl } from '@/utils/common';
|
||||
import { removeSiteInfoCache } from "@/api/cms/website";
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格选中数据
|
||||
const selection = ref<MpMenu[]>([]);
|
||||
// 当前编辑数据
|
||||
const current = ref<MpMenu | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
// 加载状态
|
||||
const loading = ref(true);
|
||||
// 页面标题
|
||||
const title = getPageTitle();
|
||||
const type = ref<number>(2);
|
||||
const refresh = ref(false);
|
||||
// 模拟器的字段
|
||||
const form = ref<any>({
|
||||
pageName: '商城',
|
||||
showCarousel: true,
|
||||
showMenuCard: true,
|
||||
showGoodsCard: true
|
||||
});
|
||||
// 菜单列表
|
||||
const list = ref<any[]>();
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({
|
||||
page,
|
||||
limit,
|
||||
where,
|
||||
orders,
|
||||
filters
|
||||
}) => {
|
||||
if (filters) {
|
||||
where.status = filters.status;
|
||||
}
|
||||
where.type = 3;
|
||||
return pageMpMenu({
|
||||
...where,
|
||||
...orders,
|
||||
page,
|
||||
limit
|
||||
});
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'menuId',
|
||||
key: 'menuId',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单图标',
|
||||
dataIndex: 'icon',
|
||||
key: 'icon',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '路由地址',
|
||||
dataIndex: 'path',
|
||||
key: 'path'
|
||||
},
|
||||
{
|
||||
title: '分类ID',
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sortNumber',
|
||||
key: 'sortNumber',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
hideInSetting: true
|
||||
}
|
||||
]);
|
||||
|
||||
/* 搜索 */
|
||||
const reload = (where?: MpMenuParam) => {
|
||||
type.value = Number(where?.type) || 0;
|
||||
refresh.value = !refresh.value;
|
||||
selection.value = [];
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
// 清除缓存
|
||||
const clearSiteInfoCache = () => {
|
||||
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId')).then(
|
||||
(msg) => {
|
||||
if (msg) {
|
||||
message.success('缓存已更新');
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: MpMenu) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeMpMenu(row.menuId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 批量删除 */
|
||||
const removeBatch = () => {
|
||||
if (!selection.value.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的记录吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchMpMenu(selection.value.map((d) => d.menuId))
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 自定义行属性 */
|
||||
const customRow = (record: MpMenu) => {
|
||||
return {
|
||||
// 行点击事件
|
||||
onClick: () => {
|
||||
// console.log(record);
|
||||
},
|
||||
// 行双击事件
|
||||
onDblclick: () => {
|
||||
openEdit(record);
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'MpMenuShop'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -1,300 +0,0 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="800"
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '编辑菜单' : '添加菜单'"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 4, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="页面" name="type">
|
||||
<a-select
|
||||
v-model:value="form.type"
|
||||
placeholder="页面"
|
||||
:style="`width: 200px`"
|
||||
>
|
||||
<a-select-option :value="2">首页</a-select-option>
|
||||
<a-select-option :value="3">商城</a-select-option>
|
||||
<a-select-option :value="1">订单</a-select-option>
|
||||
<a-select-option :value="0">我的</a-select-option>
|
||||
<a-select-option :value="4">管理</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="路由地址" name="path">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入路由地址"
|
||||
v-model:value="form.path"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="图标颜色" name="color">
|
||||
<ele-color-picker
|
||||
:show-alpha="true"
|
||||
v-model:value="form.color"
|
||||
:predefine="predefineColors"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="打开方式" name="target">
|
||||
<DictSelect
|
||||
dict-code="navType"
|
||||
class="form-item"
|
||||
placeholder="请选择链接方式"
|
||||
v-model:value="form.target"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="所在行" name="rows">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="3"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入所在行"
|
||||
v-model:value="form.rows"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="管理人员可见" name="adminShow">
|
||||
<a-switch
|
||||
checked-children="是"
|
||||
un-checked-children="否"
|
||||
:checked="form.adminShow === 1"
|
||||
@update:checked="updateAdminShow"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject, uuid } from 'ele-admin-pro';
|
||||
import { addMpMenu, updateMpMenu } from '@/api/cms/mp-menu';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FormInstance } from 'ant-design-vue/es/form';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 类型 0服务 1订单
|
||||
type?: number;
|
||||
// 修改回显的数据
|
||||
data?: MpMenu | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否显示最大化切换按钮
|
||||
const maxable = ref(true);
|
||||
// 表格选中数据
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
const images = ref<ItemType[]>([]);
|
||||
|
||||
// 用户信息
|
||||
const form = reactive<MpMenu>({
|
||||
menuId: undefined,
|
||||
parentId: 0,
|
||||
title: '',
|
||||
type: 2,
|
||||
isMpWeixin: true,
|
||||
path: undefined,
|
||||
component: undefined,
|
||||
target: undefined,
|
||||
icon: '',
|
||||
color: undefined,
|
||||
hide: undefined,
|
||||
rows: undefined,
|
||||
position: undefined,
|
||||
active: undefined,
|
||||
userId: 0,
|
||||
adminShow: undefined,
|
||||
home: undefined,
|
||||
sortNumber: 100,
|
||||
comments: '',
|
||||
status: 0
|
||||
});
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写菜单名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
type: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请选择所在页面',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
path: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写路由地址',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const chooseFile = (data: FileRecord) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.thumbnail,
|
||||
status: 'done'
|
||||
});
|
||||
form.icon = data.thumbnail;
|
||||
};
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index, 1);
|
||||
form.icon = '';
|
||||
};
|
||||
|
||||
const updateAdminShow = (value: boolean) => {
|
||||
form.adminShow = value ? 1 : 0;
|
||||
};
|
||||
|
||||
// 预设颜色
|
||||
const predefineColors = ref([
|
||||
'#40a9ff',
|
||||
'#9254de',
|
||||
'#36cfc9',
|
||||
'#73d13d',
|
||||
'#f759ab',
|
||||
'#cf1313',
|
||||
'#ff4d4f',
|
||||
'#ffa940',
|
||||
'#ffc53d',
|
||||
'#f3d3d3',
|
||||
'#1b1b1b',
|
||||
'#363636',
|
||||
'#4d4d4d',
|
||||
'#737373',
|
||||
'#a6a6a6',
|
||||
'#d9d9d9',
|
||||
'#e6e6e6',
|
||||
'#f2f2f2',
|
||||
'#f7f7f7',
|
||||
'#fafafa'
|
||||
]);
|
||||
|
||||
const { resetFields } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateMpMenu : addMpMenu;
|
||||
saveOrUpdate(formData)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
images.value = [];
|
||||
if (props.type) {
|
||||
form.type = props.type;
|
||||
}
|
||||
if (props.data) {
|
||||
assignObject(form, props.data);
|
||||
if (props.data.icon) {
|
||||
images.value.push({
|
||||
uid: uuid(),
|
||||
url: props.data.icon,
|
||||
status: 'done'
|
||||
});
|
||||
}
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
@@ -1,61 +0,0 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<a-space :size="10" style="flex-wrap: wrap">
|
||||
<a-button type="primary" class="ele-btn-icon" @click="add">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
<span>添加</span>
|
||||
</a-button>
|
||||
<!-- <a-space>-->
|
||||
<!-- <span class="ele-text-placeholder" style="margin-left: 20px"-->
|
||||
<!-- >菜单类型</span-->
|
||||
<!-- >-->
|
||||
<!-- <a-radio-group v-model:value="where.type" @change="handleSearch">-->
|
||||
<!-- <a-radio-button :value="0">会员功能</a-radio-button>-->
|
||||
<!-- <a-radio-button :value="1">订单图标</a-radio-button>-->
|
||||
<!-- </a-radio-group>-->
|
||||
<!-- </a-space>-->
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { watch } from 'vue';
|
||||
import useSearch from '@/utils/use-search';
|
||||
import { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
// 选中的角色
|
||||
selection?: [];
|
||||
}>(),
|
||||
{}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search', where?: MpMenuParam): void;
|
||||
(e: 'add'): void;
|
||||
(e: 'remove'): void;
|
||||
(e: 'batchMove'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const { where } = useSearch<MpMenu>({
|
||||
type: 0
|
||||
});
|
||||
|
||||
const handleSearch = () => {
|
||||
emit('search', where);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const add = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.selection,
|
||||
() => {}
|
||||
);
|
||||
</script>
|
||||
@@ -1,550 +0,0 @@
|
||||
<template>
|
||||
<div class="phone-layout" v-if="form">
|
||||
<div class="phone-header-black ele-fluid">
|
||||
<div class="title ele-fluid">
|
||||
<div class="title-bar">
|
||||
<span class="back"></span>
|
||||
<span>{{ form.pageName || '个人中心' }}</span>
|
||||
<a class="share" @click="onShare"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 会员信息卡片 -->
|
||||
<template v-if="form.showUserCard">
|
||||
<a-popover>
|
||||
<template #content> 点击更换背景 </template>
|
||||
<div
|
||||
class="user-card"
|
||||
:style="{
|
||||
backgroundImage: 'url(' + param.mp_user_top + ')'
|
||||
}"
|
||||
@click="
|
||||
openUserCard({
|
||||
name: 'mp_user_top',
|
||||
value: param.mp_user_top,
|
||||
comments: '小程序我的顶部背景图片',
|
||||
sortNumber: 100
|
||||
})
|
||||
"
|
||||
>
|
||||
<div class="user-avatar" @click.stop="onAvatar">
|
||||
<a-avatar :src="param.site_logo" :size="60" />
|
||||
<div class="user-info">
|
||||
<div class="nickname">昵称</div>
|
||||
<div class="phone">手机号码</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-popover>
|
||||
<UserCardEdit
|
||||
v-model:visible="showUserCardEdit"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
<!-- 订单卡片 -->
|
||||
<template v-if="form.showOrderCard">
|
||||
<div class="order-card ele-cell">
|
||||
<div
|
||||
v-for="(item, index) in order"
|
||||
:key="index"
|
||||
class="ele-cell-content ele-text-center btn-center"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-image :src="item.icon" :width="30" :preview="false" />
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="form.showToolsCard">
|
||||
<div class="tools-card">
|
||||
<div
|
||||
v-for="(item, index) in server"
|
||||
:key="index"
|
||||
class="ele-cell"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-avatar :src="item.icon" :size="24" />
|
||||
<div
|
||||
class="title ele-cell-content"
|
||||
:style="{ color: item.color || '#333333' }"
|
||||
>{{ item.title }}</div
|
||||
>
|
||||
<RightOutlined class="ele-text-secondary" />
|
||||
</div>
|
||||
</div>
|
||||
<MpMenuEdit
|
||||
v-model:visible="showMpMenuEdit"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-if="form.showMenuCard">
|
||||
<div class="phone-body" style="overflow-y: auto; overflow-x: hidden">
|
||||
<!-- 幻灯片轮播 -->
|
||||
<template v-if="form.showCarousel">
|
||||
<a-carousel arrows autoplay :dots="true">
|
||||
<template v-if="adImageList">
|
||||
<template v-for="(img, index) in adImageList" :key="index">
|
||||
<div class="ad-item">
|
||||
<a-image
|
||||
:preview="false"
|
||||
:src="img.url"
|
||||
width="100%"
|
||||
height="200px"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-carousel>
|
||||
</template>
|
||||
<!-- 导航菜单 -->
|
||||
<template v-if="form.showMenuCard">
|
||||
<div class="menu-card ele-cell">
|
||||
<div
|
||||
v-for="(item, index) in scrollList"
|
||||
:key="index"
|
||||
class="ele-cell-content ele-text-center btn-center"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-image :src="item.icon" :width="30" :preview="false" />
|
||||
<span>{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<MpMenuEdit
|
||||
v-model:visible="showMpMenuEdit"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
<!-- 商户列表 -->
|
||||
<template v-if="form.showShopCard">
|
||||
<div class="merchant-card-title">场地预定</div>
|
||||
<div
|
||||
class="merchant-card ele-cell"
|
||||
v-for="(item, index) in shopList"
|
||||
:key="index"
|
||||
>
|
||||
<a-image :src="item.image" :width="96" :preview="false" />
|
||||
<div class="merchant-info ele-cell-content">
|
||||
<div class="merchant-name">{{ item.merchantName }}</div>
|
||||
<div class="merchant-desc ele-cell-desc">
|
||||
{{ item.comments }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="">-->
|
||||
<!-- <a-button>我要去</a-button>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
<!-- 培训课程 -->
|
||||
<template v-if="form.showTtrainCard">
|
||||
<div class="merchant-card-title">培训课程</div>
|
||||
<div
|
||||
class="merchant-card ele-cell"
|
||||
v-for="(item, index) in shopList"
|
||||
:key="index"
|
||||
@click="openMpMenuEdit(item)"
|
||||
>
|
||||
<a-image :src="item.image" :width="96" :preview="false" />
|
||||
<div class="merchant-info ele-cell-content">
|
||||
<div class="merchant-name">{{ item.merchantName }}</div>
|
||||
<div class="merchant-desc ele-cell-desc">
|
||||
{{ item.comments }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<a-card
|
||||
class="buy-bar"
|
||||
:bordered="false"
|
||||
:body-style="{ padding: '12px 16px' }"
|
||||
>
|
||||
<div class="ele-cell">
|
||||
<a
|
||||
class="home-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/home`)"
|
||||
>
|
||||
<HomeOutlined class="icon" />
|
||||
<span>首页</span>
|
||||
</a>
|
||||
<a
|
||||
class="shop-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/shop`)"
|
||||
>
|
||||
<ShopOutlined class="icon" />
|
||||
<span>商城</span>
|
||||
</a>
|
||||
<a
|
||||
class="order-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/order`)"
|
||||
>
|
||||
<ProfileOutlined class="icon" />
|
||||
<span>订单</span>
|
||||
</a>
|
||||
<a
|
||||
class="user-btn ele-cell-content ele-text-secondary"
|
||||
@click="openUrl(`/mp-weixin/user`)"
|
||||
>
|
||||
<UserOutlined class="icon ele-text-danger" />
|
||||
<span class="ele-text-danger">我的</span>
|
||||
</a>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
ProfileOutlined,
|
||||
ShopOutlined,
|
||||
HomeOutlined,
|
||||
UserOutlined,
|
||||
RightOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { ref, unref, watch } from 'vue';
|
||||
import { MpWeixinParam, WebsiteField } from '@/api/cms/website/field/model';
|
||||
import { listWebsiteField } from '@/api/system/website/field';
|
||||
import { MpMenu } from '@/api/cms/mp-menu/model';
|
||||
import { listMpMenu } from '@/api/cms/mp-menu';
|
||||
import { listAd } from '@/api/cms/ad';
|
||||
import { listMerchant } from '@/api/shop/merchant';
|
||||
import { Merchant } from '@/api/shop/merchant/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { useRouter } from 'vue-router';
|
||||
const { currentRoute } = useRouter();
|
||||
const { query } = unref(currentRoute);
|
||||
|
||||
import MpMenuEdit from './mpMenuEdit.vue';
|
||||
import UserCardEdit from '@/views/cms/field/components/website-field-edit.vue';
|
||||
|
||||
const prpos = withDefaults(
|
||||
defineProps<{
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
form?: any | null;
|
||||
type?: number;
|
||||
list?: any[] | null;
|
||||
refresh?: boolean;
|
||||
}>(),
|
||||
{
|
||||
placeholder: undefined
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
const param = ref<MpWeixinParam>({});
|
||||
const showUserCardEdit = ref(false);
|
||||
const showMpMenuEdit = ref(false);
|
||||
// 当前编辑数据
|
||||
const current = ref<WebsiteField | null>(null);
|
||||
|
||||
// 幻灯片广告
|
||||
const adImageList = ref<any[]>();
|
||||
// 首页导航图标
|
||||
const scrollList = ref<any[]>();
|
||||
// 订单图标
|
||||
const order = ref<any[]>();
|
||||
// 服务图标
|
||||
const server = ref<any[]>();
|
||||
// 商户列表
|
||||
const shopList = ref<Merchant[]>();
|
||||
|
||||
const config = ref({
|
||||
selector: '#content', //容器,可使用css选择器
|
||||
branding: false,
|
||||
language: 'zh_CN', //调用放在langs文件夹内的语言包
|
||||
toolbar: false, //隐藏工具栏
|
||||
menubar: false, //隐藏菜单栏
|
||||
inline: true, //开启内联模式
|
||||
plugins: [] //选择需加载的插件
|
||||
//选中时出现的快捷工具,与插件有依赖关系
|
||||
// quickbars_selection_toolbar: 'bold italic forecolor | link blockquote quickimage',
|
||||
// init_instance_callback: function (editor) {
|
||||
// editor.setContent('这里是你的内容字符串');
|
||||
// }
|
||||
});
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openUserCard = (row?: WebsiteField) => {
|
||||
current.value = row ?? null;
|
||||
showUserCardEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openMpMenuEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showMpMenuEdit.value = true;
|
||||
};
|
||||
|
||||
const onShare = () => {};
|
||||
|
||||
const onAvatar = () => {};
|
||||
|
||||
const reload = () => {
|
||||
listWebsiteField({}).then((list) => {
|
||||
list.map((d) => {
|
||||
const key = String(d.name);
|
||||
param.value[key] = d.value;
|
||||
});
|
||||
});
|
||||
|
||||
listMpMenu({ type: 0 }).then((list) => {
|
||||
server.value = list.filter((d) => d.rows == 1);
|
||||
order.value = list.filter((d) => d.rows == 0);
|
||||
scrollList.value = list.filter((d) => d.type == 2);
|
||||
});
|
||||
|
||||
listAd({ adType: '幻灯片' }).then((res) => {
|
||||
const carouselImages = res[0].images;
|
||||
if (carouselImages) {
|
||||
adImageList.value = JSON.parse(carouselImages);
|
||||
}
|
||||
});
|
||||
|
||||
listMerchant({}).then((list) => {
|
||||
shopList.value = list;
|
||||
});
|
||||
emit('done');
|
||||
};
|
||||
|
||||
reload();
|
||||
|
||||
watch(
|
||||
() => prpos.refresh,
|
||||
(refresh) => {
|
||||
if (refresh) {
|
||||
reload();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.phone-layout {
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
width: 390px;
|
||||
height: 844px;
|
||||
background: url('@/assets/img/app-ui.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: top;
|
||||
background-size: 100%;
|
||||
//position: relative;
|
||||
padding: 0 16px;
|
||||
.phone-header-black {
|
||||
height: 99px;
|
||||
border-radius: 20px 20px 0 0;
|
||||
background-size: 100%;
|
||||
.title {
|
||||
height: 99px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
padding-bottom: 13px;
|
||||
.title-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.back {
|
||||
display: block;
|
||||
width: 50px;
|
||||
margin-left: 3px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.share {
|
||||
display: block;
|
||||
width: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.phone-body-bg {
|
||||
padding: 0 16px;
|
||||
height: 680px;
|
||||
border-radius: 0 0 44px 44px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.phone-body {
|
||||
width: 356px;
|
||||
margin-left: 17px;
|
||||
height: 630px;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 98px;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
.comments {
|
||||
padding: 20px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.form-data {
|
||||
padding: 10px 20px;
|
||||
.submit-btn {
|
||||
padding: 30px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.goods {
|
||||
.price {
|
||||
font-size: 18px;
|
||||
}
|
||||
.ele-cell-title {
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
.goods-attr {
|
||||
}
|
||||
}
|
||||
.goods-divider {
|
||||
height: 6px;
|
||||
}
|
||||
.buy-bar {
|
||||
position: fixed;
|
||||
width: 356px;
|
||||
bottom: 70px;
|
||||
//top: 881px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0 0 44px 44px;
|
||||
border-top: 1px solid var(--grey-8);
|
||||
.ele-cell-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.shop-btn,
|
||||
.kefu-btn,
|
||||
.star-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 13px;
|
||||
padding: 0 9px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.icon {
|
||||
font-size: 19px;
|
||||
}
|
||||
.buy-btn {
|
||||
display: flex;
|
||||
.add-cart {
|
||||
border-radius: 100px 0 0 100px;
|
||||
border: none;
|
||||
background-color: var(--orange-5);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
.buy-now {
|
||||
border-radius: 0 100px 100px 0;
|
||||
border: none;
|
||||
background-color: var(--red-6);
|
||||
color: #ffffff;
|
||||
height: 40px;
|
||||
width: 95px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.slick-slide) {
|
||||
overflow: hidden;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
font-size: 38px;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow) {
|
||||
color: #fff;
|
||||
background-color: rgba(31, 45, 61, 0.11);
|
||||
transition: ease all 0.3s;
|
||||
opacity: 0.3;
|
||||
z-index: 1;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:before) {
|
||||
display: none;
|
||||
}
|
||||
:deep(.slick-arrow.custom-slick-arrow:hover) {
|
||||
color: #fff;
|
||||
opacity: 0.5;
|
||||
}
|
||||
:deep(.slick-slide h3) {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
height: 170px;
|
||||
margin: 0 1px;
|
||||
background-color: var(--grey-10);
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
.user-avatar {
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.user-info {
|
||||
margin-left: 10px;
|
||||
.nickname {
|
||||
color: var(--grey-3);
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.phone {
|
||||
color: var(--grey-5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.order-card {
|
||||
width: 340px;
|
||||
height: 80px;
|
||||
margin: 0 1px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
position: absolute;
|
||||
top: 230px;
|
||||
left: 24px;
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.tools-card {
|
||||
width: 340px;
|
||||
margin: 0 1px;
|
||||
padding: 6px 16px;
|
||||
background: #ffffff;
|
||||
border-radius: 5px;
|
||||
border-color: slategrey;
|
||||
position: absolute;
|
||||
top: 324px;
|
||||
left: 24px;
|
||||
.ele-cell {
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid var(--grey-9);
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,296 +0,0 @@
|
||||
<template>
|
||||
<a-page-header :title="title" @back="() => $router.go(-1)">
|
||||
<div class="ele-cell ele-cell-align-top">
|
||||
<!-- 设计画布 -->
|
||||
<div class="body ele-cell-content ele-bg-white" style="display: ">
|
||||
<a-card :bordered="false" :body-style="{ padding: '16px' }">
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="menuId"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
tool-class="ele-toolbar-form"
|
||||
class="sys-org-table"
|
||||
>
|
||||
<template #toolbar>
|
||||
<search
|
||||
@search="reload"
|
||||
:selection="selection"
|
||||
@add="openEdit"
|
||||
@remove="removeBatch"
|
||||
@batchMove="openMove"
|
||||
/>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div class="ele-text-secondary"
|
||||
>温馨提示:选图标可以上<a
|
||||
href="https://www.iconfont.cn"
|
||||
target="_blank"
|
||||
>阿里巴巴矢量图标库</a
|
||||
>,修改完后需要<a @click="clearSiteInfoCache"
|
||||
>清除缓存</a
|
||||
>才会生效</div
|
||||
>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'icon'">
|
||||
<a-avatar :src="record.icon" :width="50" />
|
||||
</template>
|
||||
<template v-if="column.key === 'path'">
|
||||
<span class="ele-text-placeholder">{{ record.path }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'component'">
|
||||
<span class="ele-text-placeholder">{{ record.component }}</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
|
||||
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
title="确定要删除此记录吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
<a class="ele-text-danger">删除</a>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</a-card>
|
||||
</div>
|
||||
<!-- 载入模拟器 -->
|
||||
<Simulator :form="form" :type="type" :refresh="refresh" @done="reload" />
|
||||
<!-- 中间间隙 -->
|
||||
<div style="width: 500px"></div>
|
||||
<!-- 编辑弹窗 -->
|
||||
<MpMenuEdit
|
||||
v-model:visible="showEdit"
|
||||
:type="type"
|
||||
:data="current"
|
||||
@done="reload"
|
||||
/>
|
||||
</div>
|
||||
</a-page-header>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { createVNode, ref } from 'vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import type { EleProTable } from 'ele-admin-pro';
|
||||
import type {
|
||||
DatasourceFunction,
|
||||
ColumnItem
|
||||
} from 'ele-admin-pro/es/ele-pro-table/types';
|
||||
import Search from './components/search.vue';
|
||||
import MpMenuEdit from './components/mpMenuEdit.vue';
|
||||
import Simulator from './components/simulator.vue';
|
||||
import {
|
||||
pageMpMenu,
|
||||
removeMpMenu,
|
||||
removeBatchMpMenu
|
||||
} from '@/api/cms/mp-menu';
|
||||
import type { MpMenu, MpMenuParam } from '@/api/cms/mp-menu/model';
|
||||
import { getPageTitle, openUrl } from '@/utils/common';
|
||||
import { removeSiteInfoCache } from "@/api/cms/website";
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
|
||||
// 表格选中数据
|
||||
const selection = ref<MpMenu[]>([]);
|
||||
// 当前编辑数据
|
||||
const current = ref<MpMenu | null>(null);
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 是否显示批量移动弹窗
|
||||
const showMove = ref(false);
|
||||
// 页面标题
|
||||
const title = getPageTitle();
|
||||
const type = ref<number>(2);
|
||||
const refresh = ref(false);
|
||||
// 模拟器的字段
|
||||
const form = ref<any>({
|
||||
pageName: '个人中心',
|
||||
showUserCard: true,
|
||||
showOrderCard: true,
|
||||
showToolsCard: true
|
||||
});
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({
|
||||
page,
|
||||
limit,
|
||||
where,
|
||||
orders,
|
||||
filters
|
||||
}) => {
|
||||
if (filters) {
|
||||
where.status = filters.status;
|
||||
}
|
||||
where.type = 0;
|
||||
return pageMpMenu({
|
||||
...where,
|
||||
...orders,
|
||||
page,
|
||||
limit
|
||||
});
|
||||
};
|
||||
|
||||
// 表格列配置
|
||||
const columns = ref<ColumnItem[]>([
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'menuId',
|
||||
key: 'menuId',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单图标',
|
||||
dataIndex: 'icon',
|
||||
key: 'icon',
|
||||
align: 'center',
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '路由地址',
|
||||
dataIndex: 'path',
|
||||
key: 'path'
|
||||
},
|
||||
// {
|
||||
// title: '分类ID',
|
||||
// dataIndex: 'type',
|
||||
// key: 'type',
|
||||
// align: 'center'
|
||||
// },
|
||||
{
|
||||
title: '所在行',
|
||||
dataIndex: 'rows',
|
||||
key: 'rows',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sortNumber',
|
||||
key: 'sortNumber',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 180,
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
hideInSetting: true
|
||||
}
|
||||
]);
|
||||
|
||||
/* 搜索 */
|
||||
const reload = (where?: MpMenuParam) => {
|
||||
if (where?.type) {
|
||||
type.value = Number(where?.type);
|
||||
} else {
|
||||
type.value = 0;
|
||||
}
|
||||
refresh.value = !refresh.value;
|
||||
selection.value = [];
|
||||
tableRef?.value?.reload({ where: where });
|
||||
};
|
||||
|
||||
/* 打开编辑弹窗 */
|
||||
const openEdit = (row?: MpMenu) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/* 打开批量移动弹窗 */
|
||||
const openMove = () => {
|
||||
showMove.value = true;
|
||||
};
|
||||
|
||||
// 清除缓存
|
||||
const clearSiteInfoCache = () => {
|
||||
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId')).then(
|
||||
(msg) => {
|
||||
if (msg) {
|
||||
message.success('缓存已更新');
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: MpMenu) => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeMpMenu(row.menuId)
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 批量删除 */
|
||||
const removeBatch = () => {
|
||||
if (!selection.value.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确定要删除选中的记录吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
maskClosable: true,
|
||||
onOk: () => {
|
||||
const hide = message.loading('请求中..', 0);
|
||||
removeBatchMpMenu(selection.value.map((d) => d.menuId))
|
||||
.then((msg) => {
|
||||
hide();
|
||||
message.success(msg);
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
hide();
|
||||
message.error(e.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* 自定义行属性 */
|
||||
const customRow = (record: MpMenu) => {
|
||||
return {
|
||||
// 行点击事件
|
||||
onClick: () => {
|
||||
// console.log(record);
|
||||
},
|
||||
// 行双击事件
|
||||
onDblclick: () => {
|
||||
openEdit(record);
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'MpMenuUser'
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
@@ -8,7 +8,6 @@
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:customRow="customRow"
|
||||
:need-page="false"
|
||||
tool-class="ele-toolbar-form"
|
||||
class="sys-org-table"
|
||||
>
|
||||
|
||||
385
src/views/cms/navigation/components/design-edit.vue
Normal file
385
src/views/cms/navigation/components/design-edit.vue
Normal file
@@ -0,0 +1,385 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="1000"
|
||||
:visible="visible"
|
||||
:maskClosable="false"
|
||||
:maxable="true"
|
||||
:title="isUpdate ? '编辑内容' : '添加内容'"
|
||||
:body-style="{ paddingBottom: '28px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:label-col="styleResponsive ? { md: 2, sm: 5, xs: 24 } : { flex: '90px' }"
|
||||
:wrapper-col="
|
||||
styleResponsive ? { md: 21, sm: 19, xs: 24 } : { flex: '1' }
|
||||
"
|
||||
>
|
||||
<a-form-item label="页面名称" name="name">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="100"
|
||||
placeholder="关于我们"
|
||||
v-model:value="form.name"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="路由地址" name="path">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="100"
|
||||
placeholder="/about"
|
||||
v-model:value="form.path"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="组件路径" name="component">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="100"
|
||||
placeholder="请输入组件路径"
|
||||
v-model:value="form.component"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="Banner" name="photo">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
<!-- <ele-image-upload-->
|
||||
<!-- v-model:value="images"-->
|
||||
<!-- :limit="1"-->
|
||||
<!-- :drag="true"-->
|
||||
<!-- :upload-handler="uploadHandler"-->
|
||||
<!-- @upload="onUpload"-->
|
||||
<!-- />-->
|
||||
</a-form-item>
|
||||
<a-form-item label="页面内容" name="content">
|
||||
<!-- 编辑器 -->
|
||||
<div class="content">
|
||||
<tinymce-editor
|
||||
v-model:value="content"
|
||||
:disabled="disabled"
|
||||
:init="config"
|
||||
placeholder="图片直接粘贴自动上传"
|
||||
@paste="onPaste"
|
||||
/>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="状态" name="status">
|
||||
<a-radio-group v-model:value="form.status">
|
||||
<a-radio :value="0">开启</a-radio>
|
||||
<a-radio :value="1">关闭</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { uuid} from 'ele-admin-pro';
|
||||
import { addDesign, updateDesign } from '@/api/cms/design';
|
||||
import { Design } from '@/api/cms/design/model';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { FormInstance, Rule } from 'ant-design-vue/es/form';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import {uploadFile, uploadOss} from '@/api/system/file';
|
||||
import TinymceEditor from "@/components/TinymceEditor/index.vue";
|
||||
import useFormData from "@/utils/use-form-data";
|
||||
import {removeSiteInfoCache} from "@/api/cms/website";
|
||||
import {FileRecord} from "@/api/system/file/model";
|
||||
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
const disabled = ref(false);
|
||||
// 编辑器内容,双向绑定
|
||||
const content = ref<any>('');
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Design | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 已上传数据
|
||||
const images = ref<ItemType[]>([]);
|
||||
// 表格选中数据
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
|
||||
// 表单数据
|
||||
const { form, resetFields, assignFields } = useFormData<Design>({
|
||||
pageId: undefined,
|
||||
name: '',
|
||||
images: '',
|
||||
path: '',
|
||||
component: '/custom/index',
|
||||
content: '',
|
||||
type: '',
|
||||
status: 0,
|
||||
comments: '',
|
||||
sortNumber: 100,
|
||||
navigationId: undefined
|
||||
});
|
||||
|
||||
const editorRef = ref<InstanceType<typeof TinymceEditor> | null>(null);
|
||||
const config = ref({
|
||||
height: 500,
|
||||
images_upload_handler: (blobInfo, success, error) => {
|
||||
const file = blobInfo.blob();
|
||||
const formData = new FormData();
|
||||
formData.append('file', file, file.name);
|
||||
uploadOss(file).then(res => {
|
||||
success(res.url)
|
||||
}).catch((msg) => {
|
||||
error(msg);
|
||||
})
|
||||
return false;
|
||||
},
|
||||
// 自定义文件上传(这里使用把选择的文件转成 blob 演示)
|
||||
file_picker_callback: (callback: any, _value: any, meta: any) => {
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'file');
|
||||
// 设定文件可选类型
|
||||
if (meta.filetype === 'image') {
|
||||
input.setAttribute('accept', 'image/*');
|
||||
} else if (meta.filetype === 'media') {
|
||||
input.setAttribute('accept', 'video/*,.pdf');
|
||||
}
|
||||
input.onchange = () => {
|
||||
const file = input.files?.[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
if (meta.filetype === 'media') {
|
||||
if (file.size / 1024 / 1024 > 200) {
|
||||
editorRef.value?.alert({ content: '大小不能超过 200MB' });
|
||||
return;
|
||||
}
|
||||
if (!file.type.startsWith('video/')) {
|
||||
editorRef.value?.alert({ content: '只能选择视频文件' });
|
||||
return;
|
||||
}
|
||||
uploadOss(file).then(res => {
|
||||
callback(res.downloadUrl);
|
||||
})
|
||||
}
|
||||
};
|
||||
input.click();
|
||||
}
|
||||
});
|
||||
|
||||
/* 粘贴图片上传服务器并插入编辑器 */
|
||||
const onPaste = (e) => {
|
||||
const items = (e.clipboardData || e.originalEvent.clipboardData).items;
|
||||
let hasFile = false;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].type.indexOf('image') !== -1) {
|
||||
let file = items[i].getAsFile();
|
||||
const item: ItemType = {
|
||||
file,
|
||||
uid: (file as any).lastModified,
|
||||
name: file.name
|
||||
};
|
||||
uploadFile(<File>item.file)
|
||||
.then((result) => {
|
||||
const addPath = `<p><img class="content-img" src="${result.url}"></p>`;
|
||||
content.value = content.value + addPath
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
hasFile = true;
|
||||
}
|
||||
}
|
||||
if (hasFile) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<Record<string, Rule[]>>({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写页面名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
path: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写路由地址',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
component: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写组件路径',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
/* 上传事件 */
|
||||
const uploadHandler = (file: File) => {
|
||||
const item: ItemType = {
|
||||
file,
|
||||
uid: (file as any).uid,
|
||||
name: file.name
|
||||
};
|
||||
if (file.type.startsWith('video')) {
|
||||
if (file.size / 1024 / 1024 > 200) {
|
||||
message.error('大小不能超过 200MB');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (file.type.startsWith('image')) {
|
||||
if (file.size / 1024 / 1024 > 5) {
|
||||
message.error('大小不能超过 5MB');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
onUpload(item);
|
||||
};
|
||||
|
||||
// 上传文件
|
||||
const onUpload = (item: any) => {
|
||||
const { file } = item;
|
||||
uploadFile(file)
|
||||
.then((data) => {
|
||||
form.photo = data.path;
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url:
|
||||
file.type == 'video/mp4'
|
||||
? 'https://oss.wsdns.cn/20240301/6e4e32cb808245d4be336b9486961145.png'
|
||||
: data.path,
|
||||
status: 'done'
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
const chooseFile = (data: FileRecord) => {
|
||||
images.value.push({
|
||||
uid: data.id,
|
||||
url: data.downloadUrl,
|
||||
status: 'done'
|
||||
});
|
||||
form.photo = data.downloadUrl;
|
||||
}
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index,1)
|
||||
form.photo = '';
|
||||
}
|
||||
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
if (!formRef.value) {
|
||||
return;
|
||||
}
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
const formData = {
|
||||
...form,
|
||||
content: content.value
|
||||
};
|
||||
const saveOrUpdate = isUpdate.value ? updateDesign : addDesign;
|
||||
saveOrUpdate(formData)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
// 清除缓存
|
||||
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId'));
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
content.value = ''
|
||||
images.value = []
|
||||
if (props.data) {
|
||||
assignFields(props.data);
|
||||
if(props.data.content){
|
||||
content.value = props.data.content
|
||||
}
|
||||
if(props.data.photo){
|
||||
images.value.push({
|
||||
uid: uuid(),
|
||||
url: props.data.photo,
|
||||
status: 'done'
|
||||
})
|
||||
}
|
||||
isUpdate.value = !!props.data.pageId;
|
||||
} else {
|
||||
isUpdate.value = false;
|
||||
content.value = '';
|
||||
resetFields();
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
<style lang="less">
|
||||
.sdf{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -33,23 +33,13 @@
|
||||
@update:value="(value?: number) => (form.parentId = value)"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="模型" name="type">
|
||||
<a-select
|
||||
ref="select"
|
||||
v-model:value="form.type"
|
||||
style="width: 253px"
|
||||
@change="onType"
|
||||
>
|
||||
<a-select-option :value="1">单页内容</a-select-option>
|
||||
<a-select-option :value="2">新闻分类</a-select-option>
|
||||
<a-select-option :value="3">新闻详情</a-select-option>
|
||||
<a-select-option :value="4">表单设计</a-select-option>
|
||||
<a-select-option :value="5">知识文档</a-select-option>
|
||||
<a-select-option :value="6">商品分类</a-select-option>
|
||||
<a-select-option :value="7">商品详情</a-select-option>
|
||||
<a-select-option :value="9">外部链接</a-select-option>
|
||||
<a-select-option :value="0">通用模型</a-select-option>
|
||||
</a-select>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="选择页面" v-if="form.type == 1">
|
||||
<SelectDesign
|
||||
@@ -101,52 +91,55 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="form.type == 9 || form.type == 0"
|
||||
:label="form.type == 9 ? '链接地址' : '路由地址'"
|
||||
name="path"
|
||||
:extra="form.path ? `${form.path}` : ''"
|
||||
>
|
||||
<a-input
|
||||
allow-clear
|
||||
:placeholder="form.type == 9 ? '请输入链接地址' : '/about'"
|
||||
:placeholder="form.type == 9 ? '请输入链接地址' : '/about.html'"
|
||||
v-model:value="form.path"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="组件路径"
|
||||
name="component"
|
||||
v-if="form.type == 0"
|
||||
:extra="form.component ? `@/views${form.component}` : ''"
|
||||
>
|
||||
<a-form-item label="组件路径" name="component" v-if="form.type == 0">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="/about/index"
|
||||
placeholder="/custom/index"
|
||||
v-model:value="form.component"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
:width="40"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
|
||||
>
|
||||
<a-form-item label="菜单名称" name="title">
|
||||
<a-input
|
||||
allow-clear
|
||||
placeholder="请输入菜单名称"
|
||||
v-model:value="form.title"
|
||||
@pressEnter="save"
|
||||
/>
|
||||
<a-form-item label="位置" name="position">
|
||||
<a-select
|
||||
ref="select"
|
||||
v-model:value="form.position"
|
||||
style="width: 253px"
|
||||
>
|
||||
<a-select-option :value="1">顶部</a-select-option>
|
||||
<a-select-option :value="2">底部</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="模型" name="type">
|
||||
<a-select
|
||||
ref="select"
|
||||
v-model:value="form.type"
|
||||
style="width: 253px"
|
||||
@change="onType"
|
||||
>
|
||||
<a-select-option :value="0">通用模型</a-select-option>
|
||||
<a-select-option :value="1">单页内容</a-select-option>
|
||||
<a-select-option :value="2">新闻分类</a-select-option>
|
||||
<a-select-option :value="3">新闻详情</a-select-option>
|
||||
<a-select-option :value="4">表单设计</a-select-option>
|
||||
<a-select-option :value="5">知识文档</a-select-option>
|
||||
<a-select-option :value="6">商品分类</a-select-option>
|
||||
<a-select-option :value="7">商品详情</a-select-option>
|
||||
<a-select-option :value="9">外部链接</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号" name="sortNumber">
|
||||
<a-input-number
|
||||
@@ -158,16 +151,6 @@
|
||||
@pressEnter="save"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="位置" name="position">
|
||||
<a-select
|
||||
ref="select"
|
||||
v-model:value="form.position"
|
||||
style="width: 253px"
|
||||
>
|
||||
<a-select-option :value="1">顶部</a-select-option>
|
||||
<a-select-option :value="2">底部</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="是否展示" name="hide">
|
||||
<a-switch
|
||||
checked-children="是"
|
||||
@@ -176,6 +159,16 @@
|
||||
@update:checked="updateHideValue"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<SelectFile
|
||||
:placeholder="`请选择图片`"
|
||||
:limit="1"
|
||||
:data="images"
|
||||
:width="40"
|
||||
@done="chooseFile"
|
||||
@del="onDeleteItem"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<div style="margin-bottom: 22px">
|
||||
@@ -210,22 +203,17 @@
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import useFormData from '@/utils/use-form-data';
|
||||
import { Navigation } from '@/api/cms/navigation/model';
|
||||
import {
|
||||
addNavigation,
|
||||
updateNavigation,
|
||||
checkExistence
|
||||
} from '@/api/cms/navigation';
|
||||
import { addNavigation, updateNavigation } from '@/api/cms/navigation';
|
||||
import { Design } from '@/api/cms/design/model';
|
||||
import { removeSiteInfoCache } from '@/api/cms/website';
|
||||
import { isChinese, isUrl } from 'ele-admin-pro';
|
||||
import { ArticleCategory } from '@/api/cms/category/model';
|
||||
import { Article } from '@/api/cms/article/model';
|
||||
import { Form } from '@/api/cms/form/model';
|
||||
import { DocsBook } from '@/api/cms/docs-book/model';
|
||||
import { Goods } from '@/api/shop/goods/model';
|
||||
import {GoodsCategory} from "@/api/shop/goodsCategory/model";
|
||||
import {ItemType} from "ele-admin-pro/es/ele-image-upload/types";
|
||||
import {FileRecord} from "@/api/system/file/model";
|
||||
import { GoodsCategory } from '@/api/shop/goodsCategory/model';
|
||||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FileRecord } from '@/api/system/file/model';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
@@ -261,7 +249,7 @@
|
||||
// 表单数据
|
||||
const { form, resetFields, assignFields } = useFormData<Navigation>({
|
||||
navigationId: undefined,
|
||||
type: 1,
|
||||
type: 0,
|
||||
title: '',
|
||||
parentId: 0,
|
||||
path: '',
|
||||
@@ -279,14 +267,6 @@
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive<Record<string, Rule[]>>({
|
||||
type: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择导航类型',
|
||||
type: 'number',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
title: [
|
||||
{
|
||||
required: true,
|
||||
@@ -295,56 +275,66 @@
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
path: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
trigger: 'blur',
|
||||
validator: (_rule: Rule, value: string) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (!value) {
|
||||
return reject('请填写路由地址');
|
||||
}
|
||||
if (form.type == 9) {
|
||||
if (!isUrl(value)) {
|
||||
return reject('请输入正确的网址');
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
if (form.type == 1) {
|
||||
if (form.pageId == 0) {
|
||||
return reject('请选择页面');
|
||||
}
|
||||
}
|
||||
if (value.charAt(0) != '/') {
|
||||
return reject('请填写路由地址,必须是以"/"开头的英文字母+数字');
|
||||
}
|
||||
if (isChinese(value)) {
|
||||
return reject('不支持中文');
|
||||
}
|
||||
// let zg = /^[0-9a-zA-Z]*$/;
|
||||
// if (!zg.test(value)) {
|
||||
// return reject('仅支持字母和数字');
|
||||
// }
|
||||
checkExistence('path', value, form.navigationId)
|
||||
.then((msg) => {
|
||||
return reject(msg);
|
||||
})
|
||||
.catch(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
sortNumber: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入排序号',
|
||||
type: 'number',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
// component: [
|
||||
// {
|
||||
// required: true,
|
||||
// type: 'string',
|
||||
// trigger: 'blur',
|
||||
// validator: (_rule: Rule, value: string) => {
|
||||
// return new Promise<void>((resolve, reject) => {
|
||||
// if (!value) {
|
||||
// return reject('请填写组件路径');
|
||||
// }
|
||||
// if (value.charAt(0) != '/') {
|
||||
// return reject('请填写路由地址,必须是以"/"开头的英文字母+数字');
|
||||
// }
|
||||
// if (isChinese(value)) {
|
||||
// return reject('不支持中文');
|
||||
// }
|
||||
// resolve();
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// path: [
|
||||
// {
|
||||
// required: true,
|
||||
// type: 'string',
|
||||
// trigger: 'blur',
|
||||
// validator: (_rule: Rule, value: string) => {
|
||||
// return new Promise<void>((resolve, reject) => {
|
||||
// if (!value) {
|
||||
// return reject('请填写路由地址');
|
||||
// }
|
||||
// if (form.type == 9) {
|
||||
// if (!isUrl(value)) {
|
||||
// return reject('请输入正确的网址');
|
||||
// }
|
||||
// resolve();
|
||||
// }
|
||||
// if (form.type == 1) {
|
||||
// if (form.pageId == 0) {
|
||||
// return reject('请选择页面');
|
||||
// }
|
||||
// }
|
||||
// if (value.charAt(0) != '/') {
|
||||
// return reject('请填写路由地址,必须是以"/"开头的英文字母+数字');
|
||||
// }
|
||||
// if (isChinese(value)) {
|
||||
// return reject('不支持中文');
|
||||
// }
|
||||
// resolve();
|
||||
// // checkExistence('path', value, form.navigationId)
|
||||
// // .then((msg) => {
|
||||
// // return reject(msg);
|
||||
// // })
|
||||
// // .catch(() => {
|
||||
// // resolve();
|
||||
// // });
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
status: [
|
||||
{
|
||||
required: true,
|
||||
@@ -424,8 +414,6 @@
|
||||
|
||||
const chooseGoods = (data: Goods) => {
|
||||
form.goodsId = data.goodsId;
|
||||
form.title = data.title;
|
||||
form.pageName = data.title;
|
||||
form.path = '/goods/detail/' + data.goodsId;
|
||||
form.component = '/goods/search';
|
||||
};
|
||||
@@ -437,13 +425,12 @@
|
||||
status: 'done'
|
||||
});
|
||||
form.icon = data.path;
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteItem = (index: number) => {
|
||||
images.value.splice(index,1)
|
||||
images.value.splice(index, 1);
|
||||
form.icon = '';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
@@ -456,6 +443,10 @@
|
||||
const navigationForm = {
|
||||
...form
|
||||
};
|
||||
if (form.path != '' && form.path?.charAt(0) != '/') {
|
||||
message.error('路由必须以"/"开头');
|
||||
return false;
|
||||
}
|
||||
const saveOrUpdate = isUpdate.value ? updateNavigation : addNavigation;
|
||||
saveOrUpdate(navigationForm)
|
||||
.then((msg) => {
|
||||
|
||||
@@ -110,12 +110,13 @@
|
||||
<a-space>
|
||||
<a @click="openEdit(null, record.navigationId)">添加</a>
|
||||
<a-divider type="vertical" />
|
||||
<!-- <a @click="moveUp(record)">上移<ArrowUpOutlined /></a>-->
|
||||
<!-- <a-divider type="vertical" />-->
|
||||
<a @click="openEdit(record)">修改</a>
|
||||
<a-divider type="vertical" />
|
||||
<a @click="openDesign(record)">内容</a>
|
||||
<a-divider type="vertical" />
|
||||
<a-popconfirm
|
||||
placement="topRight"
|
||||
:disabled="record.home == 1"
|
||||
title="确定要删除此菜单吗?"
|
||||
@confirm="remove(record)"
|
||||
>
|
||||
@@ -135,6 +136,12 @@
|
||||
:position="position"
|
||||
@done="reload"
|
||||
/>
|
||||
<!-- 页面编辑弹窗 -->
|
||||
<DesignEdit
|
||||
v-model:visible="showDesignEdit"
|
||||
:data="design"
|
||||
@done="reload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -156,6 +163,7 @@
|
||||
} from 'ele-admin-pro/es';
|
||||
import type { EleProTable } from 'ele-admin-pro/es';
|
||||
import NavigationEdit from './components/navigation-edit.vue';
|
||||
import DesignEdit from './components/design-edit.vue';
|
||||
import {
|
||||
listNavigation,
|
||||
removeNavigation,
|
||||
@@ -164,6 +172,8 @@
|
||||
import type { Navigation, NavigationParam } from '@/api/cms/navigation/model';
|
||||
import { openPreview } from '@/utils/common';
|
||||
import { getSiteInfo } from '@/api/layout';
|
||||
import { getDesign } from '@/api/cms/design';
|
||||
import { Design } from '@/api/cms/design/model';
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
@@ -190,6 +200,7 @@
|
||||
{
|
||||
title: '组件路径',
|
||||
dataIndex: 'component',
|
||||
hideInTable: true,
|
||||
key: 'component'
|
||||
},
|
||||
{
|
||||
@@ -240,8 +251,12 @@
|
||||
|
||||
// 当前编辑数据
|
||||
const current = ref<Navigation | null>(null);
|
||||
// 当前选中页面
|
||||
const design = ref<Design>();
|
||||
// 是否显示编辑弹窗
|
||||
const showEdit = ref(false);
|
||||
// 编辑内容
|
||||
const showDesignEdit = ref(false);
|
||||
// 上级分类id
|
||||
const parentId = ref<number>();
|
||||
// 分类数据
|
||||
@@ -306,11 +321,26 @@
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
const openDesign = (row?: Navigation) => {
|
||||
// 设置默认值
|
||||
design.value = {
|
||||
navigationId: row?.navigationId,
|
||||
name: row?.title,
|
||||
path: `/page-${row?.navigationId}.html`,
|
||||
component: `/pages/custom/index`
|
||||
};
|
||||
getDesign(Number(row?.pageId))
|
||||
.then((res) => {
|
||||
design.value = res;
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
showDesignEdit.value = true;
|
||||
});
|
||||
};
|
||||
|
||||
/* 删除单个 */
|
||||
const remove = (row: Navigation) => {
|
||||
if (row.home == 1) {
|
||||
return;
|
||||
}
|
||||
if (row.children?.length) {
|
||||
message.error('请先删除子节点');
|
||||
return;
|
||||
|
||||
@@ -91,21 +91,13 @@
|
||||
<span v-if="form.version === 20">授权版</span>
|
||||
<span v-if="form.version === 30">永久授权</span>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序" name="sortNumber">
|
||||
<a-input-number
|
||||
:min="0"
|
||||
:max="9999"
|
||||
class="ele-fluid"
|
||||
placeholder="请输入排序号"
|
||||
v-model:value="form.sortNumber"
|
||||
/>
|
||||
<a-form-item label="状态" name="status">
|
||||
<a-radio-group v-model:value="form.status">
|
||||
<a-radio :value="0">运行中</a-radio>
|
||||
<a-radio :value="1">维护中</a-radio>
|
||||
<a-radio :value="2">已关闭</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<!-- <a-form-item label="状态" name="status">-->
|
||||
<!-- <a-radio-group v-model:value="form.status">-->
|
||||
<!-- <a-radio :value="0">正常</a-radio>-->
|
||||
<!-- <a-radio :value="1">已过期</a-radio>-->
|
||||
<!-- </a-radio-group>-->
|
||||
<!-- </a-form-item>-->
|
||||
</a-form>
|
||||
</ele-modal>
|
||||
</template>
|
||||
@@ -287,6 +279,7 @@
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
localStorage.setItem('Domain',`${form.prefix}${form.domain}`);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
|
||||
@@ -52,7 +52,9 @@
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="primary" ghost @click="openUrl(`${record.prefix}${record.adminUrl}`)">进入后台</a-button>
|
||||
<a @click="openUrl(`${record.prefix}${record.domain}`)">首页</a>
|
||||
<a-divider type="vertical" />
|
||||
<a @click="openUrl(record.adminUrl)">后台</a>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
@@ -84,7 +86,8 @@
|
||||
updateWebsite
|
||||
} from '@/api/cms/website';
|
||||
import type { Website, WebsiteParam } from '@/api/cms/website/model';
|
||||
import { openUrl } from '@/utils/common';
|
||||
import { openPreview, openUrl } from '@/utils/common';
|
||||
import { getSiteDomain } from '@/utils/domain';
|
||||
|
||||
// 表格实例
|
||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||
@@ -99,6 +102,7 @@
|
||||
const showMove = ref(false);
|
||||
// 加载状态
|
||||
const loading = ref(true);
|
||||
const domain = getSiteDomain();
|
||||
|
||||
// 表格数据源
|
||||
const datasource: DatasourceFunction = ({
|
||||
|
||||
Reference in New Issue
Block a user