mirror of
https://github.com/Tencent/tdesign-vue-next-starter.git
synced 2024-12-23 09:39:08 +08:00
Merge branch 'develop' of github.com:Tencent/tdesign-vue-next-starter
This commit is contained in:
commit
9840fd4360
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
*.ts text eol=lf
|
||||||
|
*.vue text eol=lf
|
||||||
|
*.tsx text eol=lf
|
||||||
|
*.jsx text eol=lf
|
||||||
|
*.html text eol=lf
|
||||||
|
*.json text eol=lf
|
7
.github/workflows/pull-request.yml
vendored
7
.github/workflows/pull-request.yml
vendored
|
@ -1,15 +1,14 @@
|
||||||
# 文件名建议统一为 pull-request.yml
|
# 文件名建议统一为 pull-request.yml
|
||||||
# 应用 test-build.yml 的 demo
|
# 应用 test-build.yml 的 demo
|
||||||
|
|
||||||
name: MAIN_PULL_REQUEST
|
name: MAIN_PULL_REQUEST
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [develop, main]
|
branches: [develop, main, site]
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
call-test-build:
|
call-test-build:
|
||||||
uses: Tencent/tdesign/.github/workflows/test-build.yml@main
|
uses: Tencent/tdesign/.github/workflows/test-build.yml@main
|
||||||
|
# install lint
|
||||||
# install lint
|
|
||||||
|
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,4 +1,6 @@
|
||||||
{
|
{
|
||||||
|
"files.eol":"\n",
|
||||||
|
"editor.tabSize": 2,
|
||||||
"eslint.format.enable": true,
|
"eslint.format.enable": true,
|
||||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"],
|
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"],
|
||||||
"[vue]": {
|
"[vue]": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "tdesign-vue-next-starter",
|
"name": "tdesign-vue-next-starter",
|
||||||
"version": "0.7.4",
|
"version": "0.7.5",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:mock": "vite --open --mode mock",
|
"dev:mock": "vite --open --mode mock",
|
||||||
"dev": "vite --open --mode development",
|
"dev": "vite --open --mode development",
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<t-icon class="header-user-avatar" name="user-circle" />
|
<t-icon class="header-user-avatar" name="user-circle" />
|
||||||
</template>
|
</template>
|
||||||
<div class="header-user-account">Tencent</div>
|
<div class="header-user-account">{{ user.userInfo.name }}</div>
|
||||||
<template #suffix><t-icon name="chevron-down" /></template>
|
<template #suffix><t-icon name="chevron-down" /></template>
|
||||||
</t-button>
|
</t-button>
|
||||||
</t-dropdown>
|
</t-dropdown>
|
||||||
|
@ -71,7 +71,7 @@ import { useRouter } from 'vue-router';
|
||||||
import LogoFull from '@/assets/assets-logo-full.svg?component';
|
import LogoFull from '@/assets/assets-logo-full.svg?component';
|
||||||
import { prefix } from '@/config/global';
|
import { prefix } from '@/config/global';
|
||||||
import { getActive } from '@/router';
|
import { getActive } from '@/router';
|
||||||
import { useSettingStore } from '@/store';
|
import { useSettingStore, useUserStore } from '@/store';
|
||||||
import type { MenuRoute } from '@/types/interface';
|
import type { MenuRoute } from '@/types/interface';
|
||||||
|
|
||||||
import MenuContent from './MenuContent.vue';
|
import MenuContent from './MenuContent.vue';
|
||||||
|
@ -111,6 +111,7 @@ const props = defineProps({
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
|
const user = useUserStore();
|
||||||
|
|
||||||
const toggleSettingPanel = () => {
|
const toggleSettingPanel = () => {
|
||||||
settingStore.updateConfig({
|
settingStore.updateConfig({
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import 'tdesign-vue-next/es/style/index.css';
|
/* eslint-disable simple-import-sort/imports */
|
||||||
import '@/style/index.less';
|
|
||||||
import './permission';
|
|
||||||
|
|
||||||
import TDesign from 'tdesign-vue-next';
|
import TDesign from 'tdesign-vue-next';
|
||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
|
|
||||||
|
@ -9,6 +6,10 @@ import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import { store } from './store';
|
import { store } from './store';
|
||||||
|
|
||||||
|
import 'tdesign-vue-next/es/style/index.css';
|
||||||
|
import '@/style/index.less';
|
||||||
|
import './permission';
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
app.use(TDesign);
|
app.use(TDesign);
|
||||||
|
|
|
@ -198,8 +198,8 @@ const onStokeDataChange = (checkedValues: string[]) => {
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
&-title {
|
&-title {
|
||||||
font: var(--td-font-headline-medium);
|
font-size: var(--td-font-size-headline-medium);
|
||||||
font-weight: 400;
|
line-height: var(--td-line-height-headline-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
&-footer {
|
&-footer {
|
||||||
|
|
|
@ -191,8 +191,8 @@ watch(
|
||||||
> span {
|
> span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
color: var(--td-text-color-primary);
|
color: var(--td-text-color-primary);
|
||||||
font: var(--td-font-headline-medium);
|
font-size: var(--td-font-size-headline-medium);
|
||||||
font-weight: 400;
|
line-height: var(--td-line-height-headline-medium);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
@change="onMaterialChange"
|
@change="onMaterialChange"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div id="lineContainer" style="width: 100%; height: 412px" />
|
<div id="lineContainer" style="width: 100%; height: 416px" />
|
||||||
</t-card>
|
</t-card>
|
||||||
</t-col>
|
</t-col>
|
||||||
<t-col :xs="12" :xl="3">
|
<t-col :xs="12" :xl="3">
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
v-for="(item, index) in PRODUCT_LIST"
|
v-for="(item, index) in PRODUCT_LIST"
|
||||||
:key="index"
|
:key="index"
|
||||||
:product="item"
|
:product="item"
|
||||||
:class="{ 'row-margin': index !== 0 }"
|
:class="{ 'row-margin': index !== 0, 'product-card': true }"
|
||||||
/>
|
/>
|
||||||
</t-col>
|
</t-col>
|
||||||
</t-row>
|
</t-row>
|
||||||
|
@ -166,6 +166,23 @@ const onMaterialChange = (value: string[]) => {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-card {
|
||||||
|
padding: var(--td-comp-paddingTB-xl) var(--td-comp-paddingTB-xl);
|
||||||
|
|
||||||
|
:deep(.t-card__header) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.t-card__body) {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: var(--td-comp-margin-xxl);
|
||||||
|
margin-bottom: var(--td-comp-margin-xxl);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.t-card__footer) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
// 统一增加8px;
|
// 统一增加8px;
|
||||||
.dashboard-detail-card {
|
.dashboard-detail-card {
|
||||||
padding: var(--td-comp-paddingTB-xxl) var(--td-comp-paddingLR-xxl);
|
padding: var(--td-comp-paddingTB-xxl) var(--td-comp-paddingLR-xxl);
|
||||||
|
@ -223,8 +240,8 @@ const onMaterialChange = (value: string[]) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
&__number {
|
&__number {
|
||||||
font: var(--td-font-headline-large);
|
font-size: var(--td-font-size-headline-medium);
|
||||||
font-weight: 400;
|
line-height: var(--td-font-size-headline-medium);
|
||||||
color: var(--td-text-color-primary);
|
color: var(--td-text-color-primary);
|
||||||
margin-bottom: var(--td-comp-margin-xxl);
|
margin-bottom: var(--td-comp-margin-xxl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ export default defineComponent({
|
||||||
height: 240px;
|
height: 240px;
|
||||||
|
|
||||||
.operator-title-icon {
|
.operator-title-icon {
|
||||||
background: var(--td-brand-color-focus);
|
background: var(--td-brand-color-light);
|
||||||
color: var(--td-brand-color);
|
color: var(--td-brand-color);
|
||||||
font-size: var(--td-comp-size-xxxl);
|
font-size: var(--td-comp-size-xxxl);
|
||||||
padding: calc(var(--td-comp-size-xxxl) - var(--td-comp-size-xl));
|
padding: calc(var(--td-comp-size-xxxl) - var(--td-comp-size-xl));
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
border-radius: var(--td-radius-medium);
|
border-radius: var(--td-radius-medium);
|
||||||
|
|
||||||
.product-sub-icon {
|
.product-sub-icon {
|
||||||
background: var(--td-brand-color-focus);
|
background: var(--td-brand-color-light);
|
||||||
color: var(--td-brand-color);
|
color: var(--td-brand-color);
|
||||||
font-size: var(--td-comp-size-xxxl);
|
font-size: var(--td-comp-size-xxxl);
|
||||||
padding: calc(var(--td-comp-size-xxxl) - var(--td-comp-size-xl));
|
padding: calc(var(--td-comp-size-xxxl) - var(--td-comp-size-xl));
|
||||||
|
|
|
@ -104,7 +104,7 @@ const setReadStatus = (item: NotificationItem) => {
|
||||||
const changeMsg = msgData.value;
|
const changeMsg = msgData.value;
|
||||||
changeMsg.forEach((e: NotificationItem) => {
|
changeMsg.forEach((e: NotificationItem) => {
|
||||||
if (e.id === item.id) {
|
if (e.id === item.id) {
|
||||||
if (e.status) e.status = false;
|
e.status = !e.status;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
store.setMsgData(changeMsg);
|
store.setMsgData(changeMsg);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import NProgress from 'nprogress'; // progress bar
|
||||||
import { MessagePlugin } from 'tdesign-vue-next';
|
import { MessagePlugin } from 'tdesign-vue-next';
|
||||||
import { RouteRecordRaw } from 'vue-router';
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
import { TOKEN_NAME } from '@/config/global';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import { getPermissionStore, getUserStore } from '@/store';
|
import { getPermissionStore, getUserStore } from '@/store';
|
||||||
import { PAGE_NOT_FOUND_ROUTE } from '@/utils/route/constant';
|
import { PAGE_NOT_FOUND_ROUTE } from '@/utils/route/constant';
|
||||||
|
@ -17,41 +18,39 @@ router.beforeEach(async (to, from, next) => {
|
||||||
const { whiteListRouters } = permissionStore;
|
const { whiteListRouters } = permissionStore;
|
||||||
|
|
||||||
const userStore = getUserStore();
|
const userStore = getUserStore();
|
||||||
const { token } = userStore;
|
|
||||||
if (token) {
|
if (userStore[TOKEN_NAME]) {
|
||||||
if (to.path === '/login') {
|
if (to.path === '/login') {
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { asyncRoutes } = permissionStore;
|
|
||||||
|
|
||||||
if (asyncRoutes && asyncRoutes.length === 0) {
|
|
||||||
const routeList = await permissionStore.buildAsyncRoutes();
|
|
||||||
routeList.forEach((item: RouteRecordRaw) => {
|
|
||||||
router.addRoute(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (to.name === PAGE_NOT_FOUND_ROUTE.name) {
|
|
||||||
// 动态添加路由后,此处应当重定向到fullPath,否则会加载404页面内容
|
|
||||||
next({ path: to.fullPath, replace: true, query: to.query });
|
|
||||||
} else {
|
|
||||||
const redirect = decodeURIComponent((from.query.redirect || to.path) as string);
|
|
||||||
next(to.path === redirect ? { ...to, replace: true } : { path: redirect });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await userStore.getUserInfo();
|
await userStore.getUserInfo();
|
||||||
|
|
||||||
|
const { asyncRoutes } = permissionStore;
|
||||||
|
|
||||||
|
if (asyncRoutes && asyncRoutes.length === 0) {
|
||||||
|
const routeList = await permissionStore.buildAsyncRoutes();
|
||||||
|
routeList.forEach((item: RouteRecordRaw) => {
|
||||||
|
router.addRoute(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (to.name === PAGE_NOT_FOUND_ROUTE.name) {
|
||||||
|
// 动态添加路由后,此处应当重定向到fullPath,否则会加载404页面内容
|
||||||
|
next({ path: to.fullPath, replace: true, query: to.query });
|
||||||
|
} else {
|
||||||
|
const redirect = decodeURIComponent((from.query.redirect || to.path) as string);
|
||||||
|
next(to.path === redirect ? { ...to, replace: true } : { path: redirect });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (router.hasRoute(to.name)) {
|
if (router.hasRoute(to.name)) {
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
next(`/`);
|
next(`/`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
MessagePlugin.error(error);
|
MessagePlugin.error(error.message);
|
||||||
next({
|
next({
|
||||||
path: '/login',
|
path: '/login',
|
||||||
query: { redirect: encodeURIComponent(to.fullPath) },
|
query: { redirect: encodeURIComponent(to.fullPath) },
|
||||||
|
|
|
@ -4,12 +4,13 @@ import { TOKEN_NAME } from '@/config/global';
|
||||||
import { store, usePermissionStore } from '@/store';
|
import { store, usePermissionStore } from '@/store';
|
||||||
|
|
||||||
const InitUserInfo = {
|
const InitUserInfo = {
|
||||||
|
name: '', // 用户名,用于展示在页面右上角头像处
|
||||||
roles: [], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用
|
roles: [], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', {
|
export const useUserStore = defineStore('user', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
token: localStorage.getItem(TOKEN_NAME) || 'main_token', // 默认token不走权限
|
[TOKEN_NAME]: 'main_token', // 默认token不走权限
|
||||||
userInfo: { ...InitUserInfo },
|
userInfo: { ...InitUserInfo },
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
|
@ -48,7 +49,7 @@ export const useUserStore = defineStore('user', {
|
||||||
|
|
||||||
const res = await mockLogin(userInfo);
|
const res = await mockLogin(userInfo);
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.token = res.data;
|
this.setToken(res.data);
|
||||||
} else {
|
} else {
|
||||||
throw res;
|
throw res;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +58,7 @@ export const useUserStore = defineStore('user', {
|
||||||
const mockRemoteUserInfo = async (token: string) => {
|
const mockRemoteUserInfo = async (token: string) => {
|
||||||
if (token === 'main_token') {
|
if (token === 'main_token') {
|
||||||
return {
|
return {
|
||||||
name: 'td_main',
|
name: 'Tencent',
|
||||||
roles: ['all'], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用
|
roles: ['all'], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -66,17 +67,19 @@ export const useUserStore = defineStore('user', {
|
||||||
roles: ['UserIndex', 'DashboardBase', 'login'], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用
|
roles: ['UserIndex', 'DashboardBase', 'login'], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const res = await mockRemoteUserInfo(this.token);
|
const res = await mockRemoteUserInfo(this[TOKEN_NAME]);
|
||||||
|
|
||||||
this.userInfo = res;
|
this.userInfo = res;
|
||||||
},
|
},
|
||||||
async logout() {
|
async logout() {
|
||||||
localStorage.removeItem(TOKEN_NAME);
|
this.removeToken();
|
||||||
this.token = '';
|
|
||||||
this.userInfo = { ...InitUserInfo };
|
this.userInfo = { ...InitUserInfo };
|
||||||
},
|
},
|
||||||
async removeToken() {
|
async removeToken() {
|
||||||
this.token = '';
|
this.setToken('');
|
||||||
|
},
|
||||||
|
async setToken(token: string) {
|
||||||
|
this[TOKEN_NAME] = token;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
persist: {
|
persist: {
|
||||||
|
@ -84,6 +87,8 @@ export const useUserStore = defineStore('user', {
|
||||||
const permissionStore = usePermissionStore();
|
const permissionStore = usePermissionStore();
|
||||||
permissionStore.initRoutes();
|
permissionStore.initRoutes();
|
||||||
},
|
},
|
||||||
|
key: 'user',
|
||||||
|
paths: [TOKEN_NAME],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -55,14 +55,6 @@
|
||||||
background: var(--td-gray-color-13);
|
background: var(--td-gray-color-13);
|
||||||
}
|
}
|
||||||
|
|
||||||
.t-default-menu:not(.t-menu--dark) .t-menu__item.t-is-active:not(.t-is-opened) {
|
|
||||||
background-color: var(--td-brand-color-1);
|
|
||||||
color: var(--td-brand-color);
|
|
||||||
.t-icon {
|
|
||||||
color: var(--td-brand-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.@{starter-prefix} {
|
.@{starter-prefix} {
|
||||||
// 布局元素调整
|
// 布局元素调整
|
||||||
&-wrapper {
|
&-wrapper {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import merge from 'lodash/merge';
|
||||||
|
|
||||||
import { TOKEN_NAME } from '@/config/global';
|
import { TOKEN_NAME } from '@/config/global';
|
||||||
import { ContentTypeEnum } from '@/constants';
|
import { ContentTypeEnum } from '@/constants';
|
||||||
|
import { getUserStore } from '@/store';
|
||||||
|
|
||||||
import { VAxios } from './Axios';
|
import { VAxios } from './Axios';
|
||||||
import type { AxiosTransform, CreateAxiosOptions } from './AxiosTransform';
|
import type { AxiosTransform, CreateAxiosOptions } from './AxiosTransform';
|
||||||
|
@ -113,7 +114,9 @@ const transform: AxiosTransform = {
|
||||||
// 请求拦截器处理
|
// 请求拦截器处理
|
||||||
requestInterceptors: (config, options) => {
|
requestInterceptors: (config, options) => {
|
||||||
// 请求之前处理config
|
// 请求之前处理config
|
||||||
const token = localStorage.getItem(TOKEN_NAME);
|
const userStore = getUserStore();
|
||||||
|
const token = userStore[TOKEN_NAME];
|
||||||
|
|
||||||
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
|
if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
|
||||||
// jwt token
|
// jwt token
|
||||||
(config as Recordable).headers.Authorization = options.authenticationScheme
|
(config as Recordable).headers.Authorization = options.authenticationScheme
|
||||||
|
|
Loading…
Reference in New Issue
Block a user