update: 重构顶部模块包含logo、PC菜单、移动端菜单、登录注册开店流程交互;干掉之前的index全局样式,全部挪到组件里面。

This commit is contained in:
mixtan 2025-06-16 14:55:34 +08:00
parent 5c3f301eb9
commit 3f9b13020a
9 changed files with 370 additions and 442 deletions

3
components.d.ts vendored
View File

@ -18,6 +18,9 @@ declare module 'vue' {
ElCol: typeof import('element-plus/es')['ElCol'] ElCol: typeof import('element-plus/es')['ElCol']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElForm: typeof import('element-plus/es')['ElForm'] ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon'] ElIcon: typeof import('element-plus/es')['ElIcon']

View File

@ -76,217 +76,3 @@ html{
color: inherit; color: inherit;
background: inherit; background: inherit;
} }
/* 页面头部 */
.header {
height: 70px;
}
/* 固定定位*/
.header-list {
width: 100%;
height: 70px;
background-color: #fff;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
.header-list .el-row{
width: 100%;
max-width: 1440px;
margin: auto;
}
.header-top {
max-width: 1440px;
width: 100%;
height: 70px;
margin: auto;
display: flex;
justify-content: center;
align-items: center;
}
/* logo */
.logo {
width: 60px;
padding-left: 20px;
}
.logo a {
background: url("../../assets/image/logo.jpg") no-repeat;
width: 48px;
height: 48px;
border-radius: 5px;
display: inline-block;
background-size: contain;
outline: none;
}
/*头部导航*/
.header-top ul {
flex: 1;
display: flex;
line-height: 60px;
}
.header-menu {
display: flex;
}
.header-menu li {
margin: 0 30px;
list-style: none;
position: relative;
color: #333;
}
.header-menu a {
text-decoration: none;
color: inherit;
}
.header-menu li span {
font-size: 16px;
}
/* 伪元素添加下滑选中线 */
.header-menu li:hover span,
.header-menu .router-link-active span
{
color: var(--bgcolor);
}
/*TODO 记得搞一个导航active的*/
.header-menu li::before
{
content: "";
position: absolute;
bottom: 0px;
left: 50%;
width: 0;
height: 100%;
border-bottom: 3px solid var(--bgcolor);
transition: all 0.2s;
}
.header-menu li:hover::before,
.header-menu .router-link-active li::before
{
content: "";
left: 0;
width: 100%;
}
.header-right {
display: flex;
justify-content: flex-end;
}
.header-right .start {
margin-right: 20px;
}
.header-right .start .start-button {
background-color: black;
border-radius: 20px;
}
.header-right .login{
margin-right: 20px;
}
.header-right .login .login-button {
}
.header-right .logout {
margin-right: 20px;
}
.header-right .logout .logout-button {
border: none;
}
/*公司*/
.company-detail {
display: flex;
}
.company-detail img {
width: 100%;
height: 100%;
object-fit: cover;
padding-right:20px;
}
.company-introduce {
padding: 20px;
letter-spacing: 2px;
line-height: 34px;
padding-bottom: 0;
}
/*轮播图*/
.banner-list {
width: 100%;
max-width: 1200px;
margin: auto;
position: relative;
}
.banner-list .el-carousel__container {
width: 100%;
padding-bottom: 50%; /* 高度自适应宽度的一半 */
position: relative;
}
.banner-list .el-carousel__item {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.banner-list .el-carousel__item img {
width: 100%;
height: 100%;
object-fit: cover;
}
/*服务*/
.container-bg {
padding: 50px 0;
}
.service-contain {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
padding-top: 30px;
}
.service-list {
width: 100%;
}
.service-list ul {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
align-items: center;
width: 100%;
}
.service-list ul li {
list-style: none;
width: 30%;
height: 211.6px;
padding: 30px 20px;
text-align: center;
box-shadow: 0 15px 25px 0 rgba(0, 0, 0, 0.1);
border-radius: 10px;
}
.service-list ul li i::before {
font-size: 70px;
color: #353535;
transition: 0.3s;
}
.service-list ul li h4 {
font-size: 26px;
font-weight: 500;
margin-top: 10px;
transition: 0.3s;
}
.service-list ul li:hover {
background: linear-gradient(90.54deg, #ec4b2b 0.42%, #f85535 102.71%);
}
.service-list ul li:hover i::before {
color: #fff;
}
.service-list ul li:hover h4 {
color: #fff;
}
.service-list ul li:hover i::before p {
color: #fff;
}

View File

@ -158,7 +158,7 @@ const register = async () => {
if (approvalRes.data.code === 0 && approvalRes.data.status === 200) { if (approvalRes.data.code === 0 && approvalRes.data.status === 200) {
const approvalStatus = approvalRes.data.approval_status; const approvalStatus = approvalRes.data.approval_status;
localStorage.setItem("approval_status", approvalStatus); localStorage.setItem("approval_status", approvalStatus);
if (approvalStatus == "4") { if (approvalStatus == "4" || approvalRes.data==null) {
router.push({ name: "start" }); router.push({ name: "start" });
} else { } else {
router.push({ name: "check" }); router.push({ name: "check" });

View File

@ -1,131 +1,161 @@
<template> <template>
<div class="header"> <div class="header">
<div class="header-list">
<el-row>
<div class="header-top">
<el-col :span="4">
<div class="logo"> <div class="logo">
<img src="@/assets/image/logo.png"> <img src="@/assets/image/logo.png" />
</div> </div>
</el-col>
<el-col :span="12">
<ul class="header-menu"> <ul class="header-menu">
<router-link v-for="(item, index) in headlist" :key="index" :to="item.path"> <router-link
<li><span>{{ item.title }}</span></li> v-for="(item, index) in headlist"
:key="index"
:to="item.path"
>
<li>
<span>{{ item.title }}</span>
</li>
</router-link> </router-link>
</ul> </ul>
</el-col>
<el-col :span="8">
<div class="header-right"> <div class="header-right">
<div class="start"> <div class="start">
<el-button color="#ea4322" @click="handleOpenStartPage"> <el-button plain type="danger" @click="handleOpenStartPage">
立即入驻 {{
approval_status == 4 || !isLoggedIn ? "立即入驻" : "查看审核状态"
}}
</el-button> </el-button>
</div> </div>
<!-- <div class="login">
<!-- <div v-if="!isLoggedIn" class="login">
<el-button plain color="#f85535" @click="openLoginForm" class="login-button"> <el-button plain color="#f85535" @click="openLoginForm" class="login-button">
商家登录 商家登录
</el-button> </el-button>
</div> --> </div> -->
<div class="avatar">
<div class="icon_avatar">
<el-icon size="20"><Avatar /></el-icon>
</div>
<el-button
link
@click="handleUserInfoClick"
:title="approval_status == 4 ? '点击立即入驻' : '点击查看审核详情'"
>
{{ mobile }}
</el-button>
</div>
<div v-if="isLoggedIn" class="logout"> <div v-if="isLoggedIn" class="logout">
<el-button type="danger" plain @click="logout" class="logout-button"> <el-button type="info" plain @click="logout" class="logout-button">
退出 退出
</el-button> </el-button>
</div> </div>
</div>
</el-col>
</div>
</el-row>
</div>
<div class="login-register-module"> <div class="sub_menu">
<span @click="handleOpenStartPage">立即入驻</span> <el-dropdown placement="bottom-end">
<span>|</span> <el-icon size="24"><Menu /></el-icon>
<span @click="openLoginForm">商家登录</span> <template #dropdown>
<span v-if="isLoggedIn" @click="logout">退出</span> <el-dropdown-menu>
<el-dropdown-item
v-for="(item, index) in headlist"
@click="toRouter(item.path)"
:key="index"
>{{ item.title }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</div> </div>
<!-- 汉堡菜单 -->
<div class="hamburger-menu" @click="toggleMenu">
<span :class="{ 'hamburger-icon': true, 'rotate-top': isMenuOpen, }"></span>
<span :class="{ 'hamburger-icon': true, 'hide-middle': isMenuOpen }"></span>
<span :class="{ 'hamburger-icon': true, 'rotate-bottom': isMenuOpen }"></span>
</div>
<div :class="{ 'mobile-menu': true, 'open': isMenuOpen }">
<ul class="mobile-menu-list">
<li v-for="(item, index) in headlist" :key="index">
<router-link :to="item.path" @click="toggleMenu">
<span>{{ item.title }}</span>
</router-link>
</li>
</ul>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, watch, onUnmounted } from 'vue'; import { ref, onMounted, watch, onUnmounted } from "vue";
import { useRouter } from 'vue-router'; import { useRouter } from "vue-router";
import { useUserStore } from '@/stores/userStore'; import { useUserStore } from "@/stores/userStore";
import { getApproval_status } from '@/api/login'; import { Menu,Avatar } from "@element-plus/icons-vue";
import { getApproval_status } from "@/api/login";
const emits = defineEmits(['open-login-form', 'open-register-form']); const emits = defineEmits(["open-login-form", "open-register-form"]);
const openLoginForm = () => { const openLoginForm = () => {
emits('open-login-form'); emits("open-login-form");
}; };
const openRegisterForm = () => { const openRegisterForm = () => {
emits('open-register-form'); emits("open-register-form");
}; };
// //
const headlist = ref([ const headlist = ref([
{ title: '首页', path: '/index' }, { title: "首页", path: "/index" },
{ title: '使用教程', path: '/help' }, { title: "使用教程", path: "/help" },
{ title: '关于我们', path: '/about' }, { title: "关于我们", path: "/about" },
]); ]);
const toRouter = (pathString: string) => { const toRouter = (pathString: string) => {
router.push({path:pathString}) router.push({ path: pathString });
} };
const router = useRouter(); const router = useRouter();
const userStore = useUserStore(); const userStore = useUserStore();
// userStore // userStore
const isLoggedIn = ref(userStore.isLoggedIn); const isLoggedIn = ref(userStore.isLoggedIn);
const mobile = ref("");
const approval_status = ref("");
// token // token
onMounted(() => { onMounted(() => {
const storedToken = localStorage.getItem('token'); const storedToken = localStorage.getItem("token");
const mobilePhone = localStorage.getItem("mobilePhone");
console.log(mobilePhone);
if (mobilePhone) {
mobile.value = mobilePhone.replace(/(^\d{3})(\d+)(\d{4})/g, "$1****$2");
}
if (storedToken) { if (storedToken) {
userStore.setToken(storedToken); // token token userStore.setToken(storedToken); // token token
} }
isLoggedIn.value = userStore.isLoggedIn; // isLoggedIn.value = userStore.isLoggedIn; //
getApproval_status().then((res) => {
if (res.code === 0 && res.status === 200) {
approval_status.value = res.data.approval_status;
console.log("res.data.approval_status", res.data.approval_status);
}
});
}); });
// userStore.isLoggedIn // userStore.isLoggedIn
watch(() => userStore.isLoggedIn, (newVal) => { watch(
() => userStore.isLoggedIn,
(newVal) => {
isLoggedIn.value = newVal; // isLoggedIn userStore.isLoggedIn isLoggedIn.value = newVal; // isLoggedIn userStore.isLoggedIn
if (isLoggedIn.value === null) console.log("登陆过期"); if (isLoggedIn.value === null) console.log("登陆过期");
// getApproval_status().then((res) => { }
// if (res.code === 0 && res.status === 200) { );
// localStorage.setItem('approval_status', res.data.approval_status);
// } else {
// }
// });
});
// //
const handleOpenStartPage = () => { const handleOpenStartPage = () => {
if (isLoggedIn.value) { if (!isLoggedIn.value) {
if (localStorage.getItem('approval_status') != null && localStorage.getItem('approval_status') === '4') {
router.push({ name: 'start' });
} else {
router.push({ name: 'check' }); // check
}
} else {
openRegisterForm(); // openRegisterForm(); //
return;
}
if (approval_status == 4) {
router.push({ name: "start" });
} else {
router.push({ name: "check" }); // check
}
};
const handleUserInfoClick = () => {
if (approval_status == 4) {
router.push({ name: "start" });
} else {
router.push({ name: "check" });
} }
}; };
@ -135,7 +165,7 @@ const logout = () => {
userStore.removeMobilePhone(); userStore.removeMobilePhone();
userStore.removeIdentity(); userStore.removeIdentity();
isLoggedIn.value = false; // isLoggedIn.value = false; //
router.push('/'); // router.push("/"); //
}; };
// //
@ -155,123 +185,137 @@ onMounted(() => {
isMenuOpen.value = false; isMenuOpen.value = false;
} }
}; };
window.addEventListener('resize', resizeHandler); window.addEventListener("resize", resizeHandler);
}); });
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('resize', resizeHandler); window.removeEventListener("resize", resizeHandler);
}); });
</script> </script>
<style scoped> <style lang="scss" scoped>
.header {
.hamburger-menu { height: 70px;
position: fixed; /* 修改为固定定位 */
top: 20px; /* 调整顶部位置 */
right: 20px; /* 调整右侧位置 */
width: 25px;
height: 25px;
cursor: pointer;
z-index: 10011; /* 设置比 header-list 更高的 z-index */
display: none;
}
.hamburger-icon {
display: block;
width: 100%; width: 100%;
height: 3px; padding: 15px 20px;
background-color: #303133; display: flex;
margin: 5px 0; align-items: center;
transition: all 0.3s ease-in-out; justify-content: space-between;
transform-origin: center; background-color: #fff;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
} }
.rotate-top { .header-menu {
transform: translateY(8px) rotate(45deg); flex: 1;
display: flex;
li {
margin: 0 20px;
list-style: none;
position: relative;
color: #333;
span {
font-size: 16px;
} }
.hide-middle { &:hover span {
opacity: 0; color: var(--bgcolor);
} }
.rotate-bottom { &::before {
transform: translateY(-8px) rotate(-45deg); content: "";
position: absolute;
bottom: 0px;
left: 50%;
width: 0;
height: 100%;
border-bottom: 3px solid var(--bgcolor);
transition: all 0.2s;
} }
.mobile-menu { &:hover::before {
position: fixed; content: "";
top: 70px; /* 移动到 header-top 下方 */
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; }
background-color: #fff; /* 修改背景颜色为白色 */
z-index: 10010;
display: flex;
flex-direction: column;
align-items: center;
transition: all 0.3s ease-in-out;
opacity: 0; /* 初始设置为透明 */
visibility: hidden; /* 初始隐藏 */
} }
.mobile-menu.open { .router-link-active li::before {
opacity: 1; /* 显示时不透明 */ content: "";
visibility: visible; /* 显示 */ left: 0;
width: 100%;
} }
.mobile-menu-list { .router-link-active span {
list-style: none; color: var(--bgcolor);
padding: 0;
margin: 0;
width: 90%;
max-width: 300px;
border-radius: 10px;
overflow: hidden;
z-index: 10010;
} }
.mobile-menu-list li { a {
padding: 10px;
line-height: 30PX;
border-bottom: 1px solid #DCDCDC;
}
.mobile-menu-list li a {
text-decoration: none; text-decoration: none;
color: inherit;
} }
.mobile-menu-list li:last-child {
border-bottom: none;
}
.login-register-module {
position: fixed;
top: 20px;
right: 60px;
z-index: 10010;
display: flex;
justify-content: center;
align-items: center;
display: none;
}
.login-register-module span {
margin-right: 10px;
line-height: 35px;
cursor: pointer;
}
.login-register-module span:hover {
color: blue;
} }
.logo { .logo {
display: inline-block; margin-right: 20px;
img { img {
height: 48px; height: 40px;
} }
} }
.icon_avatar{
width: 32px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
background: #eee;
border-radius: 100%;
}
.start {
display: flex;
justify-content: flex-end;
align-items: center;
margin-right: 5px;
}
.avatar {
display: flex;
justify-content: flex-end;
align-items: center;
}
.header-right {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 20px;
}
.sub_menu {
display: none;
cursor: pointer;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.header {
padding: 0 10px;
}
.header-menu { .header-menu {
display: none; display: none;
} }
.hamburger-menu {
display: block;
}
.logo {
img {
height: 28px;
}
}
} }
</style> </style>

View File

@ -32,11 +32,33 @@
] ]
</script> </script>
<style lang="scss"> <style lang="scss">
.banner-list {
width: 100%;
max-width: 1200px;
margin: auto;
position: relative;
margin-top: 15px;
}
.banner-list .el-carousel__container {
width: 100%;
padding-bottom: 50%; /* 高度自适应宽度的一半 */
position: relative;
}
.banner-list .el-carousel__item {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.banner-list .el-carousel__item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.banner{ .banner{
padding-top: 20px; padding-top: 20px;
}
.banner-list{
margin-top: 15px;
} }
</style> </style>

View File

@ -59,7 +59,6 @@
text-indent: 2em; text-indent: 2em;
} }
.index-title { .index-title {
width: 1200px; width: 1200px;
text-align: center; text-align: center;
@ -72,13 +71,25 @@
p { p {
font-size: 20px; font-size: 20px;
} }
} }
.company-detail { .company-detail {
display: flex;
width: 1200px; width: 1200px;
margin: 15px auto 0; margin: 15px auto 0;
} }
.company-detail img {
width: 100%;
height: 100%;
object-fit: cover;
padding-right: 20px;
}
.company-introduce {
padding: 20px;
letter-spacing: 2px;
line-height: 34px;
padding-bottom: 0;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.index-title { .index-title {

View File

@ -1,21 +1,21 @@
<script setup lang="ts"> <script setup lang="ts">
let serviceList = [ let serviceList = [
{ id: 1, title: "同城零售", icon: "iconfont icon-icon-test" }, { id: 1, title: "同城零售", icon: "iconfont icon-icon-test" },
{ id: 2, title: "企业招聘", icon: "iconfont icon-qiyezhaopin" }, { id: 2, title: "企业招聘", icon: "iconfont icon-qiyezhaopin" },
{ id: 3, title: "租赁服务", icon: "iconfont icon-zulinfuwuicon" }, { id: 3, title: "租赁服务", icon: "iconfont icon-zulinfuwuicon" },
{ id: 4, title: "二手交易", icon: "iconfont icon-ershoujiaoyi" }, { id: 4, title: "二手交易", icon: "iconfont icon-ershoujiaoyi" },
{ id: 5, title: "安装维修", icon: "iconfont icon-anzhuangweixiu" }, { id: 5, title: "安装维修", icon: "iconfont icon-anzhuangweixiu" },
{id:6,title:"家政保洁",icon:"iconfont icon-jiazhengbaojieanbao"} { id: 6, title: "家政保洁", icon: "iconfont icon-jiazhengbaojieanbao" },
] ];
</script> </script>
<template> <template>
<div class="service-contain"> <div class="service-contain">
<div class="index-title"> <div class="index-title">
<h3>我们的服务</h3> <h3>我们的服务</h3>
<p>我们拥有专业的团队和丰富的经验为您解决生活中的各种问题为您提供便捷高效优质的服务</p> <p>
我们拥有专业的团队和丰富的经验为您解决生活中的各种问题为您提供便捷高效优质的服务
</p>
</div> </div>
<div class="service-list"> <div class="service-list">
<ul> <ul>
@ -27,21 +27,70 @@
</div> </div>
</div> </div>
<div> <div></div>
</div>
</template> </template>
<style lang="scss"> <style lang="scss">
.service-contain {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
padding-top: 30px;
}
.service-list {
width: 100%;
}
.service-list ul {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
align-items: center;
width: 100%;
}
.service-list ul li {
list-style: none;
width: 30%;
height: 211.6px;
padding: 30px 20px;
text-align: center;
box-shadow: 0 15px 25px 0 rgba(0, 0, 0, 0.1);
border-radius: 10px;
}
.service-list ul li i::before {
font-size: 70px;
color: #353535;
transition: 0.3s;
}
.service-list ul li h4 {
font-size: 26px;
font-weight: 500;
margin-top: 10px;
transition: 0.3s;
}
.service-list ul li:hover {
background: linear-gradient(90.54deg, #ec4b2b 0.42%, #f85535 102.71%);
}
.service-list ul li:hover i::before {
color: #fff;
}
.service-list ul li:hover h4 {
color: #fff;
}
.service-list ul li:hover i::before p {
color: #fff;
}
.service-list { .service-list {
width: 1200px; width: 1200px;
margin: 15px 0; margin: 15px 0;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.service-list { .service-list {
width: auto; width: auto;
} }
} }
</style> </style>

View File

@ -164,8 +164,8 @@ const formatter2 = (value: number) => {
.sub { .sub {
position: absolute; position: absolute;
top: 65%; top: 58%;
transform: translateY(65%); transform: translateY(58%);
left: 150px; left: 150px;
} }
} }

View File

@ -174,6 +174,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, onMounted, watch } from "vue"; import { ref, reactive, onMounted, watch } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { Plus,Search } from "@element-plus/icons-vue"; import { Plus,Search } from "@element-plus/icons-vue";
@ -218,6 +219,7 @@ const orcImgTypeConf = {
}; };
const userStore = useUserStore(); const userStore = useUserStore();
const router = useRouter();
const processData = () => { const processData = () => {
const { provinceData, cityData: cityList, areaData } = cityData; const { provinceData, cityData: cityList, areaData } = cityData;
@ -323,6 +325,7 @@ const applyFormData = reactive({
bank_image: "", bank_image: "",
email: "", email: "",
}); });
const isLoggedIn = ref(userStore.isLoggedIn);
const license_type = ref('1') const license_type = ref('1')
const optionsPermitType = [ const optionsPermitType = [
{ {
@ -422,6 +425,11 @@ const handleGetAuditInfo = async () => {
mobile: localStorage.getItem("mobilePhone"), mobile: localStorage.getItem("mobilePhone"),
})) as any; })) as any;
if(res.data==null||res.data.approval_status==4){
router.push({ name: "start" });
return
}
let arr = JSON.parse(res.data.approval_invalid_col); let arr = JSON.parse(res.data.approval_invalid_col);
let legal_person_card = ['legal_person_id_addr','legal_person_id_period_begin','legal_person_id_period_end'] let legal_person_card = ['legal_person_id_addr','legal_person_id_period_begin','legal_person_id_period_end']
let individual_id_card = ['individual_id_addr','individual_id_period_begin','individual_id_period_end'] let individual_id_card = ['individual_id_addr','individual_id_period_begin','individual_id_period_end']
@ -984,6 +992,11 @@ const clearOtherFields = () => {
onMounted(() => { onMounted(() => {
// bankListRemoteMethod(); // bankListRemoteMethod();
if(!isLoggedIn.value){
router.push({ name: "index" });
return
}
handleGetAuditInfo(); handleGetAuditInfo();
GetStoreCategories() GetStoreCategories()