refactor(pinia): use pinia refactor store

This commit is contained in:
pengYYY 2022-02-28 18:28:14 +08:00
parent 47d0edd888
commit dd246e40b7
26 changed files with 457 additions and 518 deletions

View File

@ -21,15 +21,15 @@
"echarts": "~5.1.2",
"hex-to-hsl": "^1.0.2",
"nprogress": "^0.2.0",
"pinia": "^2.0.11",
"qrcode.vue": "^3.2.2",
"tdesign-icons-vue-next": "^0.0.6",
"tdesign-vue-next": "0.8.1",
"tdesign-vue-next": "0.9.2",
"tvision-color": "^1.3.1",
"vue": "^3.2.31",
"vue-color-kit": "^1.0.5",
"vue-router": "^4.0.11",
"vue3-clipboard": "^1.0.0",
"vuex": "^4.0.2"
"vue3-clipboard": "^1.0.0"
},
"devDependencies": {
"@commitlint/cli": "^15.0.0",

View File

@ -3,17 +3,17 @@
</template>
<script setup lang="ts">
import { computed, onMounted } from 'vue';
import { useStore } from 'vuex';
import config from '@/config/style';
import { useSettingStore } from '@/store';
const store = useStore();
const store = useSettingStore();
const mode = computed(() => {
return store.getters['setting/mode'];
return store.displayMode;
});
onMounted(() => {
store.dispatch('setting/changeTheme', { ...config });
store.updateConfig({ ...config });
});
</script>
<style lang="less">

View File

@ -3,8 +3,10 @@
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { useStore } from 'vuex';
import { getBrandColor } from '@/config/color';
import { useSettingStore } from '@/store';
const store = useSettingStore();
const panelColor =
'conic-gradient(from 90deg at 50% 50%, #FF0000 -19.41deg, #FF0000 18.76deg, #FF8A00 59.32deg, #FFE600 99.87deg, #14FF00 141.65deg, #00A3FF 177.72deg, #0500FF 220.23deg, #AD00FF 260.13deg, #FF00C7 300.69deg, #FF0000 340.59deg, #FF0000 378.76deg)';
@ -15,11 +17,9 @@ const props = defineProps({
},
});
const store = useStore();
const style = computed(() => {
const { value } = props;
const { colorList } = store.state.setting;
const { colorList } = store;
return {
background: value !== 'dynamic' ? getBrandColor(value, colorList)['@brand-color'] : panelColor,
};

View File

@ -35,5 +35,5 @@ export interface NotificationItem {
status: boolean;
collected: boolean;
date: string;
quality: 'high' | 'low' | 'middle';
quality: string;
}

View File

@ -12,7 +12,7 @@
<search :layout="layout" />
</div>
</template>
<menu-content v-show="layout !== 'side'" class="header-menu" :nav-data="menu" />
<MenuContent v-show="layout !== 'side'" class="header-menu" :nav-data="menu" />
<template #operations>
<div class="operations-container">
<!-- 搜索框 -->
@ -65,9 +65,8 @@
<script setup lang="ts">
import { PropType, computed } from 'vue';
import { useStore } from 'vuex';
import { useRouter, useRoute } from 'vue-router';
import { useSettingStore } from '@/store';
import { prefix } from '@/config/global';
import tLogoFull from '@/assets/assets-logo-full.svg?component';
import { MenuRoute } from '@/interface';
@ -107,11 +106,13 @@ const props = defineProps({
},
});
const store = useStore();
const router = useRouter();
const settingStore = useSettingStore();
const toggleSettingPanel = () => {
store.commit('setting/toggleSettingPanel', true);
settingStore.updateConfig({
showSettingPanel: true,
});
};
const active = computed(() => {
@ -141,7 +142,9 @@ const menuCls = computed(() => {
});
const changeCollapsed = () => {
store.commit('setting/toggleSidebarCompact');
settingStore.updateConfig({
isSidebarCompact: !settingStore.isSidebarCompact,
});
};
const handleNav = (url) => {

View File

@ -46,31 +46,29 @@
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { storeToRefs } from 'pinia';
import { useNotificationStore } from '@/store';
import { NotificationItem } from '@/interface';
const router = useRouter();
const store = useStore();
const { msgData } = store.state.notification;
const unreadMsg = computed(() => store.getters['notification/unreadMsg']);
const store = useNotificationStore();
const { msgData, unreadMsg } = storeToRefs(store);
const setRead = (type: string, item?: NotificationItem) => {
const changeMsg = msgData;
const changeMsg = msgData.value;
if (type === 'all') {
changeMsg.forEach((e: NotificationItem) => {
changeMsg.forEach((e) => {
e.status = false;
});
} else {
changeMsg.forEach((e: NotificationItem) => {
changeMsg.forEach((e) => {
if (e.id === item?.id) {
e.status = false;
}
});
}
store.commit('notification/setMsgData', changeMsg);
store.setMsgData(changeMsg);
};
const goDetail = () => {

View File

@ -39,8 +39,8 @@
<script setup lang="ts">
import { ref } from 'vue';
const layout = defineProps({
type: String,
defineProps({
layout: String,
});
const isSearchFocus = ref(false);

View File

@ -1,18 +1,16 @@
import { defineComponent, PropType, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { prefix } from '@/config/global';
import pgk from '../../../package.json';
import MenuContent from './MenuContent';
import tLogo from '@/assets/assets-t-logo.svg?component';
import tLogoFull from '@/assets/assets-logo-full.svg?component';
import { useSettingStore } from '@/store';
const MIN_POINT = 992 - 1;
const useComputed = (props) => {
const store = useStore();
const collapsed = computed(() => store.state.setting.isSidebarCompact);
const collapsed = computed(() => useSettingStore().isSidebarCompact);
const sideNavCls = computed(() => {
const { isCompact } = props;
@ -87,16 +85,20 @@ export default defineComponent({
},
},
setup(props) {
const store = useStore();
const router = useRouter();
const settingStore = useSettingStore();
const changeCollapsed = () => {
store.commit('setting/toggleSidebarCompact');
settingStore.updateConfig({
isSidebarCompact: !settingStore.isSidebarCompact,
});
};
const autoCollapsed = () => {
const isCompact = window.innerWidth <= MIN_POINT;
store.commit('setting/showSidebarCompact', isCompact);
settingStore.updateConfig({
isSidebarCompact: isCompact,
});
};
onMounted(() => {

View File

@ -1,5 +1,8 @@
import { defineComponent } from 'vue';
import { mapGetters } from 'vuex';
import { defineComponent, computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useRoute } from 'vue-router';
import { usePermissionStore, useSettingStore } from '@/store';
import TDesignHeader from './components/Header.vue';
import TDesignBreadcrumb from './components/Breadcrumb.vue';
import TDesignFooter from './components/Footer.vue';
@ -8,112 +11,111 @@ import TDesignContent from './components/Content.vue';
import { prefix } from '@/config/global';
import TdesignSetting from './setting.vue';
import { SettingType } from '@/interface';
import '@/style/layout.less';
const name = `${prefix}-base-layout`;
export default defineComponent({
name,
computed: {
...mapGetters({
showSidebar: 'setting/showSidebar',
showHeader: 'setting/showHeader',
showHeaderLogo: 'setting/showHeaderLogo',
showSidebarLogo: 'setting/showSidebarLogo',
showFooter: 'setting/showFooter',
mode: 'setting/mode',
menuRouters: 'permission/routers',
}),
setting(): SettingType {
return this.$store.state.setting;
},
mainLayoutCls() {
return [
{
't-layout--with-sider': this.showSidebar,
},
];
},
headerMenu() {
const { layout, splitMenu } = this.$store.state.setting;
const { menuRouters } = this;
if (layout === 'mix') {
if (splitMenu) {
return menuRouters.map((menu) => ({
setup() {
const route = useRoute();
const permissionStore = usePermissionStore();
const settingStore = useSettingStore();
const { routers: menuRouters } = storeToRefs(permissionStore);
const setting = storeToRefs(settingStore);
const mainLayoutCls = computed(() => [
{
't-layout--with-sider': settingStore.showSidebar,
},
]);
const headerMenu = computed(() => {
if (settingStore.layout === 'mix') {
if (settingStore.splitMenu) {
console.log(menuRouters);
return menuRouters.value.map((menu) => ({
...menu,
children: [],
}));
}
return [];
}
return menuRouters;
},
sideMenu() {
const { layout, splitMenu } = this.$store.state.setting;
let { menuRouters } = this;
return menuRouters.value;
});
const sideMenu = computed(() => {
const { layout, splitMenu } = settingStore;
let newMenuRouters = menuRouters.value;
if (layout === 'mix' && splitMenu) {
menuRouters.forEach((menu) => {
if (this.$route.path.indexOf(menu.path) === 0) {
menuRouters = menu.children.map((subMenu) => ({ ...subMenu, path: `${menu.path}/${subMenu.path}` }));
newMenuRouters.forEach((menu) => {
if (route.path.indexOf(menu.path) === 0) {
newMenuRouters = menu.children.map((subMenu) => ({ ...subMenu, path: `${menu.path}/${subMenu.path}` }));
}
});
}
return menuRouters;
},
},
methods: {
renderSidebar() {
return newMenuRouters;
});
const renderSidebar = () => {
return (
this.showSidebar && (
settingStore.showSidebar && (
<TDesignSideNav
showLogo={this.showSidebarLogo}
layout={this.setting.layout}
isFixed={this.setting.isSidebarFixed}
menu={this.sideMenu}
theme={this.mode}
isCompact={this.setting.isSidebarCompact}
showLogo={settingStore.showSidebarLogo}
layout={settingStore.layout}
isFixed={settingStore.isSidebarFixed}
menu={sideMenu.value}
theme={settingStore.displayMode}
isCompact={settingStore.isSidebarCompact}
/>
)
);
},
renderHeader() {
};
const renderHeader = () => {
return (
this.showHeader && (
settingStore.showHeader && (
<TDesignHeader
showLogo={this.showHeaderLogo}
theme={this.mode}
layout={this.setting.layout}
isFixed={this.setting.isHeaderFixed}
menu={this.headerMenu}
isCompact={this.setting.isSidebarCompact}
showLogo={settingStore.showHeaderLogo}
theme={settingStore.displayMode}
layout={settingStore.layout}
isFixed={settingStore.isHeaderFixed}
menu={headerMenu.value}
isCompact={settingStore.isSidebarCompact}
/>
)
);
},
renderContent() {
const { showBreadcrumb } = this.setting;
const { showFooter } = this;
};
const renderFooter = () => {
return (
<t-footer class={`${prefix}-footer-layout`}>
<TDesignFooter />
</t-footer>
);
};
const renderContent = () => {
const { showBreadcrumb, showFooter } = settingStore;
return (
<t-layout class={[`${prefix}-layout`]}>
<t-content class={`${prefix}-content-layout`}>
{showBreadcrumb && <TDesignBreadcrumb />}
<TDesignContent />
</t-content>
{showFooter && this.renderFooter()}
{showFooter && renderFooter()}
</t-layout>
);
},
};
renderFooter() {
return (
<t-footer class={`${prefix}-footer-layout`}>
<TDesignFooter />
</t-footer>
);
},
return {
setting,
mainLayoutCls,
renderSidebar,
renderHeader,
renderContent,
};
},
render() {
const { layout } = this.setting;
const header = this.renderHeader();

View File

@ -92,11 +92,11 @@
</template>
<script setup lang="ts">
import { ref, computed, watch, onMounted, watchEffect } from 'vue';
import { useStore } from 'vuex';
import { ColorPicker } from 'vue-color-kit';
import { MessagePlugin, PopupVisibleChangeContext } from 'tdesign-vue-next';
import { Color } from 'tvision-color';
import useClipboard from 'vue-clipboard3';
import { useSettingStore } from '@/store';
import 'vue-color-kit/dist/vue-color-kit.css';
@ -110,6 +110,8 @@ import SettingDarkIcon from '@/assets/assets-setting-dark.svg';
import SettingLightIcon from '@/assets/assets-setting-light.svg';
import SettingAutoIcon from '@/assets/assets-setting-auto.svg';
const settingStore = useSettingStore();
const LAYOUT_OPTION = ['side', 'top', 'mix'];
const COLOR_OPTIONS = ['default', 'cyan', 'green', 'yellow', 'orange', 'red', 'pink', 'purple', 'dynamic'];
const MODE_OPTIONS = [
@ -119,61 +121,63 @@ const MODE_OPTIONS = [
];
const formData = ref({ ...STYLE_CONFIG });
const store = useStore();
const colors = ref();
const isColoPickerDisplay = ref(false);
const showSettingPanel = computed({
get() {
return store.state.setting.showSettingPanel;
return settingStore.showSettingPanel;
},
set(newVal) {
store.commit('setting/toggleSettingPanel', newVal);
settingStore.updateConfig({
showSettingPanel: newVal,
});
},
});
const mode = computed(() => {
return store.getters['setting/mode'];
return settingStore.displayMode;
});
watch(
() => colors.value,
(newColor) => {
const { hex } = newColor;
const { setting } = store.state;
// hex
const newPalette = Color.getPaletteByGradation({
colors: [hex],
step: 10,
})[0];
const { mode } = store.state.setting;
const colorMap = generateColorMap(hex, newPalette, mode);
const { mode } = settingStore;
const colorMap = generateColorMap(hex, newPalette, mode as 'light' | 'dark');
store.commit('setting/addColor', { [hex]: colorMap });
insertThemeStylesheet(hex, colorMap, mode as 'light' | 'dark');
insertThemeStylesheet(hex, colorMap, mode);
store.dispatch('setting/changeTheme', { ...setting, brandTheme: hex });
settingStore.updateConfig({
[hex]: colorMap,
});
settingStore.changeBrandTheme(hex);
},
);
const changeColor = (val) => {
const { hex } = val;
const { setting } = store.state;
// hex
const newPalette = Color.getPaletteByGradation({
colors: [hex],
step: 10,
})[0];
const { mode } = store.state.setting;
const colorMap = generateColorMap(hex, newPalette, mode);
const { mode } = settingStore;
const colorMap = generateColorMap(hex, newPalette, mode as 'light' | 'dark');
store.commit('setting/addColor', { [hex]: colorMap });
settingStore.updateConfig({
[hex]: colorMap,
});
insertThemeStylesheet(hex, colorMap, mode);
insertThemeStylesheet(hex, colorMap, mode as 'light' | 'dark');
store.dispatch('setting/changeTheme', { ...setting, brandTheme: hex });
settingStore.changeBrandTheme(hex);
};
onMounted(() => {
@ -212,7 +216,9 @@ const getModeIcon = (mode: string) => {
};
const handleCloseDrawer = () => {
store.commit('setting/toggleSettingPanel', false);
settingStore.updateConfig({
showSettingPanel: false,
});
};
const getThumbnailUrl = (name: string): string => {
@ -220,7 +226,7 @@ const getThumbnailUrl = (name: string): string => {
};
watchEffect(() => {
store.dispatch('setting/changeTheme', formData.value);
settingStore.updateConfig(formData.value);
});
</script>
<style lang="less">

View File

@ -3,11 +3,11 @@ import { createApp } from 'vue';
import TDesign from 'tdesign-vue-next';
import 'tdesign-vue-next/es/style/index.css';
import VueClipboard from 'vue3-clipboard';
import { store } from './store';
import router from './router';
import '@/style/index.less';
import './permission';
import App from './App.vue';
const app = createApp(App);

View File

@ -2,9 +2,7 @@ import dayjs from 'dayjs';
import * as echarts from 'echarts/core';
import { Color } from 'tvision-color';
import { getBrandColor, defaultLightColor, defaultDarkColor } from '@/config/color';
import store from '@/store';
const { state } = store;
import { getSettingStore } from '@/store';
/**
*
@ -14,8 +12,8 @@ const { state } = store;
* @returns {}
*/
export function getColorFromTheme(theme: string) {
const { setting } = state as any;
const { colorList, mode } = setting;
const settingStore = getSettingStore();
const { colorList, mode } = settingStore;
const isDarkMode = mode === 'dark';
let themeColorList = [];
const themeColor = getBrandColor(theme, colorList);
@ -41,8 +39,9 @@ export function getColorFromTheme(theme: string) {
/** 图表颜色 */
function chartListColor(): Array<string> {
const { setting } = state as any;
const res = getColorFromTheme(setting.brandTheme);
const settingStore = getSettingStore();
const { brandTheme } = settingStore;
const res = getColorFromTheme(brandTheme);
return res;
}
@ -1078,7 +1077,7 @@ export function getPieChartDataSet({
textColor,
placeholderColor,
containerColor,
}: { radius: number } & Record<string, string>) {
}: { radius?: number } & Record<string, string>) {
return {
color: chartListColor(),
tooltip: {

View File

@ -2,7 +2,7 @@
<div>
<t-row :gutter="[16, 16]">
<t-col v-for="(item, index) in PANE_LIST" :key="item.title" :xs="6" :xl="3">
<card :subtitle="item.title" :style="{ height: '168px' }" :class="{ 'main-color': index == 0 }">
<card :subtitle="item.title" size="small" :style="{ height: '168px' }" :class="{ 'main-color': index == 0 }">
<div class="dashboard-item">
<div class="dashboard-item-top">
<span :style="{ fontSize: `${resizeTime * 36}px` }">{{ item.number }}</span>
@ -204,12 +204,12 @@
</template>
<script setup lang="ts">
import { onMounted, watch, ref, onUnmounted, nextTick, computed } from 'vue';
import { useStore } from 'vuex';
import * as echarts from 'echarts/core';
import { TooltipComponent, LegendComponent, GridComponent } from 'echarts/components';
import { PieChart, LineChart, BarChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
import { useSettingStore } from '@/store';
import { LAST_7_DAYS } from '@/utils/date';
//
@ -241,10 +241,10 @@ const getThisMonth = (checkedValues?: string[]) => {
return `${date.getFullYear()}-${startMonth}${date2.getFullYear()}-${endMonth}`;
};
const store = useStore();
const store = useSettingStore();
const resizeTime = ref(1);
const chartColors = computed(() => store.state.setting.chartColors);
const chartColors = computed(() => store.chartColors);
// moneyCharts
let moneyContainer: HTMLElement;
@ -276,7 +276,7 @@ const renderStokeChart = () => {
stokeContainer = document.getElementById('stokeContainer');
}
stokeChart = echarts.init(stokeContainer);
stokeChart.setOption(constructInitDataset({ dateTime: LAST_7_DAYS, ...chartColors.value }));
stokeChart.setOption(constructInitDataset({ dateTime: LAST_7_DAYS, ...(chartColors.value as any) }));
};
// monitorChart
@ -355,14 +355,14 @@ onUnmounted(() => {
const currentMonth = ref(getThisMonth());
watch(
() => store.state.setting.brandTheme,
() => store.brandTheme,
() => {
changeChartsTheme([refundChart, stokeChart, monitorChart, countChart]);
},
);
watch(
() => store.state.setting.mode,
() => store.mode,
() => {
[moneyChart, refundChart, stokeChart, monitorChart, countChart].forEach((item) => {
item.dispose();
@ -374,10 +374,10 @@ watch(
const onCurrencyChange = (checkedValues: string[]) => {
currentMonth.value = getThisMonth(checkedValues);
monitorChart.setOption(getLineChartDataSet({ dateTime: checkedValues, ...chartColors.value }));
monitorChart.setOption(getLineChartDataSet({ dateTime: checkedValues, ...(chartColors.value as any) }));
};
const onStokeDataChange = (checkedValues: string[]) => {
stokeChart.setOption(constructInitDataset({ dateTime: checkedValues, ...chartColors.value }));
stokeChart.setOption(constructInitDataset({ dateTime: checkedValues, ...(chartColors.value as any) }));
};
const rehandleClickOp = (val: MouseEvent) => {
console.log(val);

View File

@ -59,7 +59,6 @@
</template>
<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, watch, computed } from 'vue';
import { useStore } from 'vuex';
import * as echarts from 'echarts/core';
import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components';
@ -70,14 +69,16 @@ import ProductCard from '@/components/card/Card.vue';
import { changeChartsTheme, getFolderLineDataSet, getScatterDataSet } from '../base/index';
import { PANE_LIST_DATA, PRODUCT_LIST } from './constants';
import { LAST_7_DAYS } from '@/utils/date';
import { useSettingStore } from '@/store';
import Trend from '@/components/trend/index.vue';
import Card from '@/components/card/index.vue';
echarts.use([GridComponent, LegendComponent, TooltipComponent, LineChart, ScatterChart, CanvasRenderer]);
const store = useStore();
const chartColors = computed(() => store.state.setting.chartColors);
const store = useSettingStore();
const chartColors = computed(() => store.chartColors);
// lineChart logic
let lineContainer: HTMLElement;
let lineChart: echarts.ECharts;
@ -126,14 +127,14 @@ onUnmounted(() => {
});
watch(
() => store.state.setting.mode,
() => store.mode,
() => {
renderCharts();
},
);
watch(
() => store.state.setting.brandTheme,
() => store.brandTheme,
() => {
changeChartsTheme([lineChart, scatterChart]);
},
@ -144,8 +145,8 @@ const onSatisfyChange = () => {
};
const onMaterialChange = (value: string[]) => {
const chartColors = computed(() => store.state.setting.chartColors);
lineChart.setOption(getFolderLineDataSet({ dateTime: value, ...chartColors.value }));
const chartColors = computed(() => store.chartColors);
lineChart.setOption(getFolderLineDataSet({ dateTime: value, ...(chartColors.value as any) }));
};
</script>
<style lang="less" scoped>

View File

@ -70,12 +70,12 @@
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch, computed } from 'vue';
import { useStore } from 'vuex';
import * as echarts from 'echarts/core';
import { TitleComponent, ToolboxComponent, TooltipComponent, GridComponent, LegendComponent } from 'echarts/components';
import { BarChart, LineChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
import { useSettingStore } from '@/store';
import { changeChartsTheme, getSmoothLineDataSet, get2ColBarChartDataSet } from '../../dashboard/base/index';
import { BASE_INFO_DATA, TABLE_COLUMNS as columns } from './constants';
@ -96,9 +96,9 @@ echarts.use([
CanvasRenderer,
]);
const store = useStore();
const store = useSettingStore();
const chartColors = computed(() => store.state.setting.chartColors);
const chartColors = computed(() => store.chartColors);
const data = ref([]);
const pagination = ref({
defaultPageSize: 10,
@ -173,7 +173,7 @@ onMounted(() => {
});
watch(
() => store.state.setting.brandTheme,
() => store.brandTheme,
() => {
changeChartsTheme([monitorChart, dataChart]);
},

View File

@ -50,11 +50,12 @@
</div>
</template>
<script setup lang="ts">
import { ref, computed, ComputedRef } from 'vue';
import { useStore } from 'vuex';
import { ref, computed } from 'vue';
import { storeToRefs } from 'pinia';
import { NOTIFICATION_TYPES } from '@/constants';
import { NotificationItem } from '@/interface';
import EmptyIcon from '@/assets/assets-empty.svg?component';
import { useNotificationStore } from '@/store';
const TAB_LIST = [
{
@ -76,14 +77,13 @@ const tabValue = ref('msgData');
const visible = ref(false);
const selectedItem = ref<NotificationItem>();
const store = useStore();
const store = useNotificationStore();
const { msgData, unreadMsg, readMsg } = storeToRefs(store);
const { msgData } = store.state.notification;
const msgDataList: ComputedRef<NotificationItem[]> = computed(() => {
if (tabValue.value === 'msgData') return msgData;
if (tabValue.value === 'unreadMsg') return store.getters['notification/unreadMsg'];
if (tabValue.value === 'readMsg') return store.getters['notification/readMsg'];
const msgDataList = computed(() => {
if (tabValue.value === 'msgData') return msgData.value;
if (tabValue.value === 'unreadMsg') return unreadMsg.value;
if (tabValue.value === 'readMsg') return readMsg.value;
return [];
});
@ -93,25 +93,25 @@ const handleClickDeleteBtn = (item: NotificationItem) => {
};
const setReadStatus = (item: NotificationItem) => {
const changeMsg = msgData;
const changeMsg = msgData.value;
changeMsg.forEach((e: NotificationItem) => {
if (e.id === item.id) {
if (e.status) e.status = false;
}
});
store.commit('notification/setMsgData', changeMsg);
store.setMsgData(changeMsg);
};
const deleteMsg = () => {
const item = selectedItem.value;
const changeMsg = msgData;
const changeMsg = msgData.value;
changeMsg.forEach((e: NotificationItem, index: number) => {
if (e.id === item?.id) {
changeMsg.splice(index, 1);
}
});
visible.value = false;
store.commit('notification/setMsgData', changeMsg);
store.setMsgData(changeMsg);
};
</script>
<style lang="less" scoped>

View File

@ -16,12 +16,14 @@
</template>
<script setup lang="ts">
import { useStore } from 'vuex';
import LogoFullIcon from '@/assets/assets-logo-full.svg?component';
import { useSettingStore } from '@/store';
const store = useStore();
const settingStore = useSettingStore();
const toggleSettingPanel = () => {
store.commit('setting/toggleSettingPanel', true);
settingStore.updateConfig({
showSettingPanel: true,
});
};
const navToGitHub = () => {

View File

@ -73,10 +73,12 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import QrcodeVue from 'qrcode.vue';
import { MessagePlugin } from 'tdesign-vue-next';
import { useCounter } from '@/hooks';
import { useUserStore } from '@/store';
const userStore = useUserStore();
const INITIAL_DATA = {
phone: '',
@ -105,12 +107,12 @@ const switchType = (val: string) => {
};
const router = useRouter();
const store = useStore();
const onSubmit = async ({ validateResult }) => {
if (validateResult === true) {
try {
await store.dispatch('user/login', formData.value);
await userStore.login(formData.value);
MessagePlugin.success('登陆成功');
router.push({
path: '/dashboard/base',

View File

@ -91,11 +91,11 @@
</template>
<script setup lang="ts">
import { nextTick, onMounted, onUnmounted, watch, computed } from 'vue';
import { useStore } from 'vuex';
import * as echarts from 'echarts/core';
import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components';
import { LineChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
import { useSettingStore } from '@/store';
import { LAST_7_DAYS } from '@/utils/date';
import { USER_INFO_LIST, TEAM_MEMBERS, PRODUCT_LIST } from './constants';
@ -111,8 +111,8 @@ echarts.use([GridComponent, TooltipComponent, LineChart, CanvasRenderer, LegendC
let lineContainer: HTMLElement;
let lineChart: echarts.ECharts;
const store = useStore();
const chartColors = computed(() => store.state.setting.chartColors);
const store = useSettingStore();
const chartColors = computed(() => store.chartColors);
const onLineChange = (value) => {
lineChart.setOption(getFolderLineDataSet(value));
@ -166,7 +166,7 @@ const getIcon = (type) => {
};
watch(
() => store.state.setting.brandTheme,
() => store.brandTheme,
() => {
changeChartsTheme([lineChart]);
},

View File

@ -2,42 +2,47 @@ import { MessagePlugin } from 'tdesign-vue-next';
import NProgress from 'nprogress'; // progress bar
import 'nprogress/nprogress.css'; // progress bar style
import store from '@/store';
import { getPermissionStore, getUserStore } from '@/store';
import router from '@/router';
const permissionStore = getPermissionStore();
const userStore = getUserStore();
NProgress.configure({ showSpinner: false });
const whiteListRouters = store.getters['permission/whiteListRouters'];
const { whiteListRouters } = permissionStore;
router.beforeEach(async (to, from, next) => {
NProgress.start();
const token = store.getters['user/token'];
const { token } = userStore;
if (token) {
if (to.path === '/login') {
setTimeout(() => {
store.dispatch('user/logout');
store.dispatch('permission/restore');
userStore.logout();
permissionStore.restore();
});
next();
return;
}
const roles = store.getters['user/roles'];
const { roles } = userStore;
if (roles && roles.length > 0) {
next();
} else {
try {
await store.dispatch('user/getUserInfo');
await userStore.getUserInfo();
await store.dispatch('permission/initRoutes', store.getters['user/roles']);
const { roles } = userStore;
await permissionStore.initRoutes(roles);
next({ ...to });
} catch (error) {
console.log(error);
MessagePlugin.error(error);
await store.commit('user/removeToken');
await userStore.removeToken();
next(`/login?redirect=${to.path}`);
NProgress.done();
}

View File

@ -4,6 +4,8 @@ import baseRouters from './modules/base';
import componentsRouters from './modules/components';
import othersRouters from './modules/others';
// 关于单层路由meta 中设置 { single: true } 即可为单层路由,{ hidden: true } 即可在侧边栏隐藏该路由
// 存放动态路由
export const asyncRouterList: Array<RouteRecordRaw> = [...baseRouters, ...componentsRouters, ...othersRouters];

View File

@ -1,16 +1,12 @@
import { createStore } from 'vuex';
import user from './modules/user';
import notification from './modules/notification';
import setting from './modules/setting';
import permission from './modules/permission';
import { createPinia } from 'pinia';
export const store = createStore({
modules: {
user,
setting,
notification,
permission,
},
});
const store = createPinia();
export { store };
export * from './modules/notification';
export * from './modules/permission';
export * from './modules/user';
export * from './modules/setting';
export default store;

View File

@ -1,85 +1,76 @@
// 定义的state初始值
import { defineStore } from 'pinia';
import { NotificationItem } from '@/interface';
const state = {
msgData: [
{
id: '123',
content: '腾讯大厦一楼改造施工项目 已通过审核!',
type: '合同动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'high',
},
{
id: '124',
content: '三季度生产原材料采购项目 开票成功!',
type: '票务动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'low',
},
{
id: '125',
content: '2021-01-01 10:00的【国家电网线下签约】会议即将开始请提前10分钟前往 会议室1 进行签到!',
type: '会议通知',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'middle',
},
{
id: '126',
content: '一季度生产原材料采购项目 开票成功!',
type: '票务动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'low',
},
{
id: '127',
content: '二季度生产原材料采购项目 开票成功!',
type: '票务动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'low',
},
{
id: '128',
content: '三季度生产原材料采购项目 开票成功!',
type: '票务动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'low',
},
],
};
type NotificationStateType = typeof state;
type MsgDataType = typeof state.msgData;
const mutations = {
setMsgData(state: NotificationStateType, data: MsgDataType) {
state.msgData = data;
const msgData = [
{
id: '123',
content: '腾讯大厦一楼改造施工项目 已通过审核!',
type: '合同动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'high',
},
};
{
id: '124',
content: '三季度生产原材料采购项目 开票成功!',
type: '票务动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'low',
},
{
id: '125',
content: '2021-01-01 10:00的【国家电网线下签约】会议即将开始请提前10分钟前往 会议室1 进行签到!',
type: '会议通知',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'middle',
},
{
id: '126',
content: '一季度生产原材料采购项目 开票成功!',
type: '票务动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'low',
},
{
id: '127',
content: '二季度生产原材料采购项目 开票成功!',
type: '票务动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'low',
},
{
id: '128',
content: '三季度生产原材料采购项目 开票成功!',
type: '票务动态',
status: true,
collected: false,
date: '2021-01-01 08:00',
quality: 'low',
},
];
const getters = {
unreadMsg: (state: NotificationStateType) => state.msgData.filter((item: NotificationItem) => item.status),
readMsg: (state: NotificationStateType) => state.msgData.filter((item: NotificationItem) => !item.status),
};
type MsgDataType = typeof msgData;
const actions = {};
export default {
namespaced: true,
state,
mutations,
actions,
getters,
};
export const useNotificationStore = defineStore('notification', {
state: () => ({
msgData,
}),
getters: {
unreadMsg: (state) => state.msgData.filter((item: NotificationItem) => item.status),
readMsg: (state) => state.msgData.filter((item: NotificationItem) => !item.status),
},
actions: {
setMsgData(data: MsgDataType) {
this.msgData = data;
},
},
});

View File

@ -1,4 +1,6 @@
import { defineStore } from 'pinia';
import router, { asyncRouterList, page404 } from '@/router';
import { store } from '@/store';
function filterPermissionsRouters(routes, roles) {
const res = [];
@ -18,57 +20,38 @@ function filterPermissionsRouters(routes, roles) {
return res;
}
const state = {
whiteListRouters: ['/login'],
routers: [],
};
export const usePermissionStore = defineStore('permission', {
state: () => ({
whiteListRouters: ['/login'],
routers: [],
}),
actions: {
async initRoutes(roles) {
let accessedRouters;
const mutations = {
setRouters: (state, routers) => {
state.routers = routers;
// special token
if (roles.includes('ALL_ROUTERS')) {
accessedRouters = asyncRouterList;
} else {
accessedRouters = filterPermissionsRouters(asyncRouterList, roles);
}
this.routers = accessedRouters;
// register routers
accessedRouters.concat(page404).forEach((item) => {
router.addRoute(item);
});
},
async restore() {
this.routers.concat(page404).forEach((item) => {
if (router.hasRoute(item.name)) router.removeRoute(item.name);
});
this.routers = [];
},
},
};
});
const getters = {
routers: (state) => {
return state.routers;
},
whiteListRouters: (state) => {
return state.whiteListRouters;
},
};
const actions = {
async initRoutes({ commit }, roles) {
let accessedRouters;
// special token
if (roles.includes('ALL_ROUTERS')) {
accessedRouters = asyncRouterList;
} else {
accessedRouters = filterPermissionsRouters(asyncRouterList, roles);
}
commit('setRouters', accessedRouters);
// register routers
state.routers.concat(page404).forEach((item) => {
router.addRoute(item);
});
},
async restore({ commit, state }) {
// remove routers
state.routers.concat(page404).forEach((item) => {
router.removeRoute(item.name);
});
commit('setRouters', []);
},
};
export default {
namespaced: true,
state,
mutations,
actions,
getters,
};
export function getPermissionStore() {
return usePermissionStore(store);
}

View File

@ -1,7 +1,8 @@
import { defineStore } from 'pinia';
import { COLOR_TOKEN, LIGHT_CHART_COLORS, DARK_CHART_COLORS } from '@/config/color';
import STYLE_CONFIG from '@/config/style';
import { COLOR_TOKEN, ColorSeries, ColorToken, LIGHT_CHART_COLORS, DARK_CHART_COLORS } from '@/config/color';
import { store } from '@/store';
// 定义的state初始值
const state = {
...STYLE_CONFIG,
showSettingPanel: false,
@ -9,98 +10,60 @@ const state = {
chartColors: LIGHT_CHART_COLORS,
};
type IInitStateType = typeof state;
export const useSettingStore = defineStore('setting', {
state: () => state,
getters: {
showSidebar: (state) => state.layout !== 'top',
showSidebarLogo: (state) => state.layout === 'side',
showHeaderLogo: (state) => state.layout !== 'side',
displayMode: (state) => {
if (state.mode === 'auto') {
const media = window.matchMedia('(prefers-color-scheme:dark)');
if (media.matches) {
return 'dark';
}
return 'light';
}
return state.mode;
},
},
actions: {
async changeMode(mode: 'dark' | 'light' | 'auto') {
let theme = mode;
interface IStateType extends IInitStateType {
isAsideFooter: boolean;
showSettingPanel: boolean;
if (mode === 'auto') {
const media = window.matchMedia('(prefers-color-scheme:dark)');
if (media.matches) {
theme = 'dark';
} else {
theme = 'light';
}
}
const isDarkMode = theme === 'dark';
document.documentElement.setAttribute('theme-mode', isDarkMode ? 'dark' : '');
this.chartColor = isDarkMode ? DARK_CHART_COLORS : LIGHT_CHART_COLORS;
},
changeBrandTheme(brandTheme: string) {
document.documentElement.setAttribute('theme-color', brandTheme);
},
updateConfig(payload) {
for (const key in payload) {
if (payload[key] !== undefined) {
this[key] = payload[key];
}
if (key === 'mode') {
this.changeMode(payload[key]);
}
if (key === 'brandTheme') {
this.changeBrandTheme(payload[key]);
}
}
},
},
});
export function getSettingStore() {
return useSettingStore(store);
}
const mutations = {
update(state: IStateType, payload: IStateType) {
state.showBreadcrumb = payload.showBreadcrumb;
state.mode = payload.mode;
state.layout = payload.layout;
state.isSidebarCompact = payload.isSidebarCompact;
state.splitMenu = payload.splitMenu;
state.isFooterAside = payload.isFooterAside;
state.isSidebarFixed = payload.isSidebarFixed;
state.isHeaderFixed = payload.isHeaderFixed;
state.showHeader = payload.showHeader;
state.showFooter = payload.showFooter;
state.backgroundTheme = payload.backgroundTheme;
state.brandTheme = payload.brandTheme;
},
toggleSidebarCompact(state: IStateType) {
state.isSidebarCompact = !state.isSidebarCompact;
},
showSidebarCompact(state: IStateType, payload: boolean) {
state.isSidebarCompact = payload;
},
toggleSettingPanel(state: IStateType, payload: boolean) {
state.showSettingPanel = payload;
},
addColor(state: IStateType, payload: ColorSeries) {
state.colorList = { ...state.colorList, ...payload };
},
changeChartColor(state: IStateType, payload: ColorToken) {
state.chartColors = { ...payload };
},
};
const getters = {
showHeader: (state: IStateType) => state.showHeader,
showSidebar: (state: IStateType) => state.layout !== 'top',
showSidebarLogo: (state: IStateType) => state.layout === 'side',
showHeaderLogo: (state: IStateType) => state.layout !== 'side',
showFooter: (state: IStateType) => state.showFooter,
mode: (state: IStateType) => {
if (state.mode === 'auto') {
const media = window.matchMedia('(prefers-color-scheme:dark)');
if (media.matches) {
return 'dark';
}
return 'light';
}
return state.mode;
},
};
const actions = {
async changeTheme({ commit, dispatch }, payload) {
dispatch('changeMode', payload);
dispatch('changeBrandTheme', payload);
commit('update', payload);
},
async changeMode({ commit }, payload: IStateType) {
let theme = payload.mode;
if (payload.mode === 'auto') {
const media = window.matchMedia('(prefers-color-scheme:dark)');
if (media.matches) {
theme = 'dark';
} else {
theme = 'light';
}
}
const isDarkMode = theme === 'dark';
document.documentElement.setAttribute('theme-mode', isDarkMode ? 'dark' : '');
commit('changeChartColor', isDarkMode ? DARK_CHART_COLORS : LIGHT_CHART_COLORS);
},
changeBrandTheme(_: { state: IStateType }, payload: IStateType) {
const { brandTheme } = payload;
document.documentElement.setAttribute('theme-color', brandTheme);
},
};
export default {
namespaced: true,
state,
mutations,
actions,
getters,
};

View File

@ -1,102 +1,86 @@
import { defineStore } from 'pinia';
import { TOKEN_NAME } from '@/config/global';
import { store } from '@/store';
const InitUserInfo = {
roles: [],
};
// 定义的state初始值
const state = {
token: localStorage.getItem(TOKEN_NAME) || 'main_token', // 默认token不走权限
userInfo: InitUserInfo,
};
const mutations = {
setToken(state, token) {
localStorage.setItem(TOKEN_NAME, token);
state.token = token;
export const useUserStore = defineStore('user', {
state: () => ({
token: localStorage.getItem(TOKEN_NAME) || 'main_token', // 默认token不走权限
userInfo: InitUserInfo,
}),
getters: {
roles: (state) => {
return state.userInfo?.roles;
},
},
removeToken(state) {
localStorage.removeItem(TOKEN_NAME);
state.token = '';
},
setUserInfo(state, userInfo) {
state.userInfo = userInfo;
},
};
const getters = {
token: (state) => {
return state.token;
},
roles: (state) => {
return state.userInfo?.roles;
},
};
const actions = {
async login({ commit }, userInfo) {
const mockLogin = async (userInfo) => {
// 登录请求流程
console.log(userInfo);
// const { account, password } = userInfo;
// if (account !== 'td') {
// return {
// code: 401,
// message: '账号不存在',
// };
// }
// if (['main_', 'dev_'].indexOf(password) === -1) {
// return {
// code: 401,
// message: '密码错误',
// };
// }
// const token = {
// main_: 'main_token',
// dev_: 'dev_token',
// }[password];
return {
code: 200,
message: '登陆成功',
data: 'main_token',
};
};
const res = await mockLogin(userInfo);
if (res.code === 200) {
commit('setToken', res.data);
} else {
throw res;
}
},
async getUserInfo({ commit, state }) {
const mockRemoteUserInfo = async (token) => {
if (token === 'main_token') {
actions: {
async login(userInfo) {
const mockLogin = async (userInfo) => {
// 登录请求流程
console.log(userInfo);
// const { account, password } = userInfo;
// if (account !== 'td') {
// return {
// code: 401,
// message: '账号不存在',
// };
// }
// if (['main_', 'dev_'].indexOf(password) === -1) {
// return {
// code: 401,
// message: '密码错误',
// };
// }
// const token = {
// main_: 'main_token',
// dev_: 'dev_token',
// }[password];
return {
name: 'td_main',
roles: ['ALL_ROUTERS'],
code: 200,
message: '登陆成功',
data: 'main_token',
};
}
return {
name: 'td_dev',
roles: ['userIndex', 'dashboardBase', 'login'],
};
};
const res = await mockRemoteUserInfo(state.token);
const res = await mockLogin(userInfo);
if (res.code === 200) {
this.token = res.data;
} else {
throw res;
}
},
async getUserInfo() {
const mockRemoteUserInfo = async (token) => {
if (token === 'main_token') {
return {
name: 'td_main',
roles: ['ALL_ROUTERS'],
};
}
return {
name: 'td_dev',
roles: ['userIndex', 'dashboardBase', 'login'],
};
};
commit('setUserInfo', res);
const res = await mockRemoteUserInfo(this.token);
this.userInfo = res;
},
async logout() {
localStorage.removeItem(TOKEN_NAME);
this.token = '';
this.userInfo = InitUserInfo;
},
async removeToken() {
this.token = '';
},
},
async logout({ commit }) {
commit('removeToken');
commit('setUserInfo', InitUserInfo);
},
};
});
export default {
namespaced: true,
state,
mutations,
actions,
getters,
};
export function getUserStore() {
return useUserStore(store);
}