Merge branch 'develop' of github.com:Tencent/tdesign-vue-next-starter into site

This commit is contained in:
Uyarn 2023-05-30 11:40:41 +08:00
commit f7fb462479
36 changed files with 519 additions and 130 deletions

View File

@ -1,2 +1,11 @@
// commit-lint config // commit-lint config
module.exports = { extends: ['@commitlint/config-conventional'] }; module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test', 'types'],
],
},
};

View File

@ -1,6 +1,6 @@
{ {
"name": "tdesign-vue-next-starter", "name": "@tencent/tdesign-vue-next-starter",
"version": "0.7.5", "version": "0.7.6",
"scripts": { "scripts": {
"dev:mock": "vite --open --mode mock", "dev:mock": "vite --open --mode mock",
"dev": "vite --open --mode development", "dev": "vite --open --mode development",
@ -28,10 +28,10 @@
"pinia-plugin-persistedstate": "^3.1.0", "pinia-plugin-persistedstate": "^3.1.0",
"qrcode.vue": "^3.3.4", "qrcode.vue": "^3.3.4",
"qs": "^6.11.1", "qs": "^6.11.1",
"tdesign-icons-vue-next": "^0.1.8", "tdesign-icons-vue-next": "^0.1.11",
"tdesign-theme-generator": "^1.0.4", "tdesign-theme-generator": "^1.0.5",
"tdesign-vue-next": "^1.3.1", "tdesign-vue-next": "^1.3.4",
"tvision-color": "^1.5.0", "tvision-color": "^1.6.0",
"vue": "^3.2.47", "vue": "^3.2.47",
"vue-clipboard3": "^2.0.0", "vue-clipboard3": "^2.0.0",
"vue-router": "~4.1.6" "vue-router": "~4.1.6"
@ -84,12 +84,10 @@
"lint-staged": { "lint-staged": {
"*.{js,jsx,vue,ts,tsx}": [ "*.{js,jsx,vue,ts,tsx}": [
"prettier --write", "prettier --write",
"npm run lint:fix", "npm run lint:fix"
"git add ."
], ],
"*.{html,vue,vss,sass,less}": [ "*.{html,vue,vss,sass,less}": [
"npm run stylelint:fix", "npm run stylelint:fix"
"git add ."
] ]
} }
} }

View File

@ -1,2 +1 @@
export const prefix = 'tdesign-starter'; export const prefix = 'tdesign-starter';
export const TOKEN_NAME = 'tdesign-starter';

View File

@ -34,12 +34,14 @@ export const CONTRACT_PAYMENT_TYPES = {
RECEIPT: 1, RECEIPT: 1,
}; };
// 通知的优先级对应的TAG类型 // 标签类型
export const NOTIFICATION_TYPES = { type TagTheme = 'default' | 'success' | 'primary' | 'warning' | 'danger';
low: 'primary', // 通知的优先级对应的标签类型
middle: 'warning', export const NOTIFICATION_TYPES: Map<string, TagTheme> = new Map([
high: 'danger', ['low', 'primary'],
}; ['middle', 'warning'],
['high', 'danger'],
]);
// 通用请求头 // 通用请求头
export enum ContentTypeEnum { export enum ContentTypeEnum {

View File

@ -39,7 +39,7 @@ export const useChart = (domId: string): Ref<echarts.ECharts> => {
* @returns * @returns
*/ */
export const useCounter = (duration = 60): [Ref<number>, () => void] => { export const useCounter = (duration = 60): [Ref<number>, () => void] => {
let intervalTimer; let intervalTimer: ReturnType<typeof setInterval>;
onUnmounted(() => { onUnmounted(() => {
clearInterval(intervalTimer); clearInterval(intervalTimer);
}); });

View File

@ -130,7 +130,7 @@ const changeCollapsed = () => {
}); });
}; };
const handleNav = (url) => { const handleNav = (url: string) => {
router.push(url); router.push(url);
}; };

View File

@ -24,7 +24,7 @@
:min-column-width="128" :min-column-width="128"
:popup-props="{ :popup-props="{
overlayClassName: 'route-tabs-dropdown', overlayClassName: 'route-tabs-dropdown',
onVisibleChange: (visible, ctx) => handleTabMenuClick(visible, ctx, routeItem.path), onVisibleChange: (visible: boolean, ctx: PopupVisibleChangeContext) => handleTabMenuClick(visible, ctx, routeItem.path),
visible: activeTabPath === routeItem.path, visible: activeTabPath === routeItem.path,
}" }"
> >
@ -70,12 +70,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { PopupVisibleChangeContext } from 'tdesign-vue-next';
import { computed, nextTick, ref } from 'vue'; import { computed, nextTick, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { prefix } from '@/config/global'; import { prefix } from '@/config/global';
import { useSettingStore, useTabsRouterStore } from '@/store'; import { useSettingStore, useTabsRouterStore } from '@/store';
import type { TRouterInfo } from '@/types/interface'; import type { TRouterInfo, TTabRemoveOptions } from '@/types/interface';
import LBreadcrumb from './Breadcrumb.vue'; import LBreadcrumb from './Breadcrumb.vue';
import LContent from './Content.vue'; import LContent from './Content.vue';
@ -95,12 +96,12 @@ const handleChangeCurrentTab = (path: string) => {
router.push({ path, query: route.query }); router.push({ path, query: route.query });
}; };
const handleRemove = ({ value: path, index }) => { const handleRemove = (options: TTabRemoveOptions) => {
const { tabRouters } = tabsRouterStore; const { tabRouters } = tabsRouterStore;
const nextRouter = tabRouters[index + 1] || tabRouters[index - 1]; const nextRouter = tabRouters[options.index + 1] || tabRouters[options.index - 1];
tabsRouterStore.subtractCurrentTabRouter({ path, routeIdx: index }); tabsRouterStore.subtractCurrentTabRouter({ path: options.value as string, routeIdx: options.index });
if (path === route.path) router.push({ path: nextRouter.path, query: nextRouter.query }); if ((options.value as string) === route.path) router.push({ path: nextRouter.path, query: nextRouter.query });
}; };
const handleRefresh = (route: TRouterInfo, routeIdx: number) => { const handleRefresh = (route: TRouterInfo, routeIdx: number) => {
@ -147,7 +148,7 @@ const handleOperationEffect = (type: 'other' | 'ahead' | 'behind', routeIndex: n
activeTabPath.value = null; activeTabPath.value = null;
}; };
const handleTabMenuClick = (visible: boolean, ctx, path: string) => { const handleTabMenuClick = (visible: boolean, ctx: PopupVisibleChangeContext, path: string) => {
if (ctx.trigger === 'document') activeTabPath.value = null; if (ctx.trigger === 'document') activeTabPath.value = null;
if (visible) activeTabPath.value = path; if (visible) activeTabPath.value = path;
}; };

View File

@ -16,6 +16,7 @@ import { computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { usePermissionStore, useSettingStore } from '@/store'; import { usePermissionStore, useSettingStore } from '@/store';
import type { MenuRoute } from '@/types/interface';
import LSideNav from './SideNav.vue'; import LSideNav from './SideNav.vue';
@ -26,7 +27,7 @@ const { routers: menuRouters } = storeToRefs(permissionStore);
const sideMenu = computed(() => { const sideMenu = computed(() => {
const { layout, splitMenu } = settingStore; const { layout, splitMenu } = settingStore;
let newMenuRouters = menuRouters.value; let newMenuRouters = menuRouters.value as Array<MenuRoute>;
if (layout === 'mix' && splitMenu) { if (layout === 'mix' && splitMenu) {
newMenuRouters.forEach((menu) => { newMenuRouters.forEach((menu) => {
if (route.path.indexOf(menu.path) === 0) { if (route.path.indexOf(menu.path) === 0) {

346
src/layouts/setting.vue Normal file
View File

@ -0,0 +1,346 @@
<template>
<t-drawer
v-model:visible="showSettingPanel"
size="408px"
:footer="false"
header="页面配置"
:close-btn="true"
class="setting-drawer-container"
@close-btn-click="handleCloseDrawer"
>
<div class="setting-container">
<t-form ref="form" :data="formData" label-align="left">
<div class="setting-group-title">主题模式</div>
<t-radio-group v-model="formData.mode">
<div v-for="(item, index) in MODE_OPTIONS" :key="index" class="setting-layout-drawer">
<div>
<t-radio-button :key="index" :value="item.type"
><component :is="getModeIcon(item.type)"
/></t-radio-button>
<p :style="{ textAlign: 'center', marginTop: '8px' }">{{ item.text }}</p>
</div>
</div>
</t-radio-group>
<div class="setting-group-title">主题色</div>
<t-radio-group v-model="formData.brandTheme">
<div v-for="(item, index) in DEFAULT_COLOR_OPTIONS" :key="index" class="setting-layout-drawer">
<t-radio-button :key="index" :value="item" class="setting-layout-color-group">
<color-container :value="item" />
</t-radio-button>
</div>
<div class="setting-layout-drawer">
<t-popup
destroy-on-close
expand-animation
placement="bottom-right"
trigger="click"
:visible="isColoPickerDisplay"
:overlay-style="{ padding: 0 }"
@visible-change="onPopupVisibleChange"
>
<template #content>
<t-color-picker-panel
:on-change="changeColor"
:color-modes="['monochrome']"
format="HEX"
:swatch-colors="[]"
/>
</template>
<t-radio-button :value="dynamicColor" class="setting-layout-color-group dynamic-color-btn">
<color-container :value="dynamicColor" />
</t-radio-button>
</t-popup>
</div>
</t-radio-group>
<div class="setting-group-title">导航布局</div>
<t-radio-group v-model="formData.layout">
<div v-for="(item, index) in LAYOUT_OPTION" :key="index" class="setting-layout-drawer">
<t-radio-button :key="index" :value="item">
<thumbnail :src="getThumbnailUrl(item)" />
</t-radio-button>
</div>
</t-radio-group>
<t-form-item v-show="formData.layout === 'mix'" label="分割菜单(混合模式下有效)" name="splitMenu">
<t-switch v-model="formData.splitMenu" />
</t-form-item>
<t-form-item v-show="formData.layout === 'mix'" label="固定 Sidebar" name="isSidebarFixed">
<t-switch v-model="formData.isSidebarFixed" />
</t-form-item>
<div class="setting-group-title">元素开关</div>
<t-form-item v-show="formData.layout === 'side'" label="显示 Header" name="showHeader">
<t-switch v-model="formData.showHeader" />
</t-form-item>
<t-form-item label="显示 Breadcrumbs" name="showBreadcrumb">
<t-switch v-model="formData.showBreadcrumb" />
</t-form-item>
<t-form-item label="显示 Footer" name="showFooter">
<t-switch v-model="formData.showFooter" />
</t-form-item>
<t-form-item label="使用 多标签Tab页" name="isUseTabsRouter">
<t-switch v-model="formData.isUseTabsRouter"></t-switch>
</t-form-item>
</t-form>
<div class="setting-info">
<p>请复制后手动修改配置文件: /src/config/style.ts</p>
<t-button theme="primary" variant="text" @click="handleCopy"> 复制配置项 </t-button>
</div>
</div>
</t-drawer>
</template>
<script setup lang="ts">
import type { PopupVisibleChangeContext } from 'tdesign-vue-next';
import { MessagePlugin } from 'tdesign-vue-next';
import { computed, onMounted, ref, watchEffect } from 'vue';
import useClipboard from 'vue-clipboard3';
import SettingAutoIcon from '@/assets/assets-setting-auto.svg';
import SettingDarkIcon from '@/assets/assets-setting-dark.svg';
import SettingLightIcon from '@/assets/assets-setting-light.svg';
import ColorContainer from '@/components/color/index.vue';
import Thumbnail from '@/components/thumbnail/index.vue';
import { DEFAULT_COLOR_OPTIONS } from '@/config/color';
import STYLE_CONFIG from '@/config/style';
import { useSettingStore } from '@/store';
const settingStore = useSettingStore();
const LAYOUT_OPTION = ['side', 'top', 'mix'];
const MODE_OPTIONS = [
{ type: 'light', text: '明亮' },
{ type: 'dark', text: '暗黑' },
{ type: 'auto', text: '跟随系统' },
];
const initStyleConfig = () => {
const styleConfig = STYLE_CONFIG;
for (const key in styleConfig) {
if (Object.prototype.hasOwnProperty.call(styleConfig, key)) {
(styleConfig[key as keyof typeof STYLE_CONFIG] as any) = settingStore[key as keyof typeof STYLE_CONFIG];
}
}
return styleConfig;
};
const dynamicColor = computed(() => {
const isDynamic = DEFAULT_COLOR_OPTIONS.indexOf(formData.value.brandTheme) === -1;
return isDynamic ? formData.value.brandTheme : '';
});
const formData = ref({ ...initStyleConfig() });
const isColoPickerDisplay = ref(false);
const showSettingPanel = computed({
get() {
return settingStore.showSettingPanel;
},
set(newVal: boolean) {
settingStore.updateConfig({
showSettingPanel: newVal,
});
},
});
const changeColor = (hex: string) => {
formData.value.brandTheme = hex;
};
onMounted(() => {
document.querySelector('.dynamic-color-btn').addEventListener('click', () => {
isColoPickerDisplay.value = true;
});
});
const onPopupVisibleChange = (visible: boolean, context: PopupVisibleChangeContext) => {
if (!visible && context.trigger === 'document') {
isColoPickerDisplay.value = visible;
}
};
const handleCopy = () => {
const text = JSON.stringify(formData.value, null, 4);
const { toClipboard } = useClipboard();
toClipboard(text)
.then(() => {
MessagePlugin.closeAll();
MessagePlugin.success('复制成功');
})
.catch(() => {
MessagePlugin.closeAll();
MessagePlugin.error('复制失败');
});
};
const getModeIcon = (mode: string) => {
if (mode === 'light') {
return SettingLightIcon;
}
if (mode === 'dark') {
return SettingDarkIcon;
}
return SettingAutoIcon;
};
const handleCloseDrawer = () => {
settingStore.updateConfig({
showSettingPanel: false,
});
};
const getThumbnailUrl = (name: string): string => {
return `https://tdesign.gtimg.com/tdesign-pro/setting/${name}.png`;
};
watchEffect(() => {
if (formData.value.brandTheme) settingStore.updateConfig(formData.value);
});
</script>
<!-- teleport导致drawer scoped样式问题无法生效 先规避下 -->
<!-- eslint-disable-next-line vue-scoped-css/enforce-style-type -->
<style lang="less">
.tdesign-setting {
z-index: 100;
position: fixed;
bottom: 200px;
right: 0;
transition: transform 0.3s cubic-bezier(0.7, 0.3, 0.1, 1), visibility 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
height: 40px;
width: 40px;
border-radius: 20px 0 0 20px;
transition: all 0.3s;
.t-icon {
margin-left: 8px;
}
.tdesign-setting-text {
font-size: 12px;
display: none;
}
&:hover {
width: 96px;
.tdesign-setting-text {
display: inline-block;
}
}
}
.setting-layout-color-group {
display: inline-flex;
justify-content: center;
align-items: center;
border-radius: 50% !important;
padding: 6px !important;
border: 2px solid transparent !important;
> .t-radio-button__label {
display: inline-flex;
}
}
.tdesign-setting-close {
position: fixed;
bottom: 200px;
right: 300px;
}
.setting-group-title {
font-size: 14px;
line-height: 22px;
margin: 32px 0 24px 0;
text-align: left;
font-family: PingFang SC;
font-style: normal;
font-weight: 500;
color: var(--td-text-color-primary);
}
.setting-link {
cursor: pointer;
color: var(--td-brand-color);
margin-bottom: 8px;
}
.setting-info {
position: absolute;
padding: 24px;
bottom: 0;
left: 0;
line-height: 20px;
font-size: 12px;
text-align: center;
color: var(--td-text-color-placeholder);
width: 100%;
background: var(--td-bg-color-container);
}
.setting-drawer-container {
.setting-container {
padding-bottom: 100px;
}
.t-radio-group.t-size-m {
min-height: 32px;
width: 100%;
justify-content: space-between;
align-items: center;
}
.t-radio-group.t-size-m .t-radio-button {
height: auto;
}
.setting-layout-drawer {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 16px;
.t-radio-button {
display: inline-flex;
max-height: 78px;
padding: 8px;
border-radius: var(--td-radius-default);
border: 2px solid var(--td-component-border);
> .t-radio-button__label {
display: inline-flex;
}
}
.t-is-checked {
border: 2px solid var(--td-brand-color) !important;
}
.t-form__controls-content {
justify-content: end;
}
}
.t-form__controls-content {
justify-content: end;
}
}
.setting-route-theme {
.t-form__label {
min-width: 310px !important;
color: var(--td-text-color-secondary);
}
}
.setting-color-theme {
.setting-layout-drawer {
.t-radio-button {
height: 32px;
}
&:last-child {
margin-right: 0;
}
}
}
</style>

View File

@ -1,4 +1,5 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { EChartsOption } from 'echarts';
import { TChartColor } from '@/config/color'; import { TChartColor } from '@/config/color';
import { getRandomArray } from '@/utils/charts'; import { getRandomArray } from '@/utils/charts';
@ -307,7 +308,7 @@ export function getPieChartDataSet({
textColor, textColor,
placeholderColor, placeholderColor,
containerColor, containerColor,
}: { radius?: number } & Record<string, string>) { }: { radius?: number } & Record<string, string>): EChartsOption {
return { return {
color: getChartListColor(), color: getChartListColor(),
tooltip: { tooltip: {

View File

@ -83,14 +83,14 @@ export const TABLE_COLUMNS_DATA = [
ellipsis: true, ellipsis: true,
colKey: 'index', colKey: 'index',
title: '申请号', title: '申请号',
sorter: (a, b) => a.index.substr(3) - b.index.substr(3), sorter: (a: any, b: any) => a.index.substr(3) - b.index.substr(3),
}, },
{ {
width: 200, width: 200,
ellipsis: true, ellipsis: true,
colKey: 'pdName', colKey: 'pdName',
title: '产品名称', title: '产品名称',
sorter: (a, b) => a.pdName.length - b.pdName.length, sorter: (a: any, b: any) => a.pdName.length - b.pdName.length,
}, },
{ {
width: 200, width: 200,
@ -103,7 +103,7 @@ export const TABLE_COLUMNS_DATA = [
ellipsis: true, ellipsis: true,
colKey: 'purchaseNum', colKey: 'purchaseNum',
title: '采购数量', title: '采购数量',
sorter: (a, b) => a.purchaseNum - b.purchaseNum, sorter: (a: any, b: any) => a.purchaseNum - b.purchaseNum,
}, },
{ {
width: 160, width: 160,
@ -116,7 +116,7 @@ export const TABLE_COLUMNS_DATA = [
ellipsis: true, ellipsis: true,
colKey: 'updateTime', colKey: 'updateTime',
title: '创建时间', title: '创建时间',
sorter: (a, b) => Date.parse(a.updateTime) - Date.parse(b.updateTime), sorter: (a: any, b: any) => Date.parse(a.updateTime) - Date.parse(b.updateTime),
}, },
{ {
align: 'left' as const, align: 'left' as const,

View File

@ -170,16 +170,16 @@ onMounted(() => {
}); });
const visible = ref(false); const visible = ref(false);
const sortChange = (val) => { const sortChange = (val: unknown) => {
console.log(val); console.log(val);
}; };
const rehandleChange = (changeParams, triggerAndData) => { const rehandleChange = (changeParams: unknown, triggerAndData: unknown) => {
console.log('统一Change', changeParams, triggerAndData); console.log('统一Change', changeParams, triggerAndData);
}; };
const listClick = () => { const listClick = () => {
visible.value = true; visible.value = true;
}; };
const deleteClickOp = (columns) => { const deleteClickOp = (columns: { rowIndex: number }) => {
data.value.splice(columns.rowIndex, 1); data.value.splice(columns.rowIndex, 1);
}; };
const onConfirm = () => { const onConfirm = () => {

View File

@ -87,7 +87,7 @@ export const TABLE_COLUMNS = [
ellipsis: true, ellipsis: true,
colKey: 'name', colKey: 'name',
title: '项目名称', title: '项目名称',
sorter: (a, b) => a.name.substr(10) - b.name.substr(10), sorter: (a: any, b: any) => a.name.substr(10) - b.name.substr(10),
}, },
{ {
width: '280', width: '280',
@ -101,7 +101,7 @@ export const TABLE_COLUMNS = [
ellipsis: true, ellipsis: true,
colKey: 'updateTime', colKey: 'updateTime',
title: '创建时间', title: '创建时间',
sorter: (a, b) => Date.parse(a.updateTime) - Date.parse(b.updateTime), sorter: (a: any, b: any) => Date.parse(a.updateTime) - Date.parse(b.updateTime),
}, },
{ {
align: 'left' as const, align: 'left' as const,

View File

@ -147,8 +147,6 @@ onMounted(() => {
dataChart.setOption(get2ColBarChartDataSet({ ...chartColors.value })); dataChart.setOption(get2ColBarChartDataSet({ ...chartColors.value }));
}); });
const intervalTimer = null;
/// / chartSize update /// / chartSize update
const updateContainer = () => { const updateContainer = () => {
monitorChart.resize({ monitorChart.resize({
@ -163,7 +161,6 @@ const updateContainer = () => {
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('resize', updateContainer); window.removeEventListener('resize', updateContainer);
clearInterval(intervalTimer);
}); });
const onAlertChange = () => { const onAlertChange = () => {
@ -182,10 +179,10 @@ watch(
}, },
); );
const sortChange = (val) => { const sortChange = (val: unknown) => {
console.log(val); console.log(val);
}; };
const rehandleChange = (changeParams, triggerAndData) => { const rehandleChange = (changeParams: unknown, triggerAndData: unknown) => {
console.log('统一Change', changeParams, triggerAndData); console.log('统一Change', changeParams, triggerAndData);
}; };
const listClick = () => { const listClick = () => {
@ -194,7 +191,7 @@ const listClick = () => {
const onConfirm = () => { const onConfirm = () => {
visible.value = false; visible.value = false;
}; };
const deleteClickOp = (e) => { const deleteClickOp = (e: { rowIndex: number }) => {
data.value.splice(e.rowIndex, 1); data.value.splice(e.rowIndex, 1);
}; };
</script> </script>

View File

@ -6,7 +6,7 @@
<t-list v-if="msgDataList.length > 0" class="secondary-msg-list" :split="true"> <t-list v-if="msgDataList.length > 0" class="secondary-msg-list" :split="true">
<t-list-item v-for="(item, index) in msgDataList" :key="index"> <t-list-item v-for="(item, index) in msgDataList" :key="index">
<p :class="['content', { unread: item.status }]" @click="setReadStatus(item)"> <p :class="['content', { unread: item.status }]" @click="setReadStatus(item)">
<t-tag size="medium" :theme="NOTIFICATION_TYPES[item.quality]" variant="light"> <t-tag size="medium" :theme="NOTIFICATION_TYPES.get(item.quality)" variant="light">
{{ item.type }} {{ item.type }}
</t-tag> </t-tag>
{{ item.content }} {{ item.content }}

View File

@ -1,4 +1,4 @@
import { FormRule } from 'tdesign-vue-next'; import type { FormRule, UploadFile } from 'tdesign-vue-next';
export const FORM_RULES: Record<string, FormRule[]> = { export const FORM_RULES: Record<string, FormRule[]> = {
name: [{ required: true, message: '请输入合同名称', type: 'error' }], name: [{ required: true, message: '请输入合同名称', type: 'error' }],
@ -23,7 +23,7 @@ export const INITIAL_DATA = {
payment: '1', payment: '1',
amount: 0, amount: 0,
comment: '', comment: '',
files: [], files: [] as Array<UploadFile>,
}; };
export const TYPE_OPTIONS = [ export const TYPE_OPTIONS = [

View File

@ -164,6 +164,7 @@ export default {
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import type { SubmitContext, UploadFailContext, UploadFile } from 'tdesign-vue-next';
import { MessagePlugin } from 'tdesign-vue-next'; import { MessagePlugin } from 'tdesign-vue-next';
import { ref } from 'vue'; import { ref } from 'vue';
@ -174,12 +175,12 @@ const formData = ref({ ...INITIAL_DATA });
const onReset = () => { const onReset = () => {
MessagePlugin.warning('取消新建'); MessagePlugin.warning('取消新建');
}; };
const onSubmit = ({ validateResult }) => { const onSubmit = (ctx: SubmitContext) => {
if (validateResult === true) { if (ctx.validateResult === true) {
MessagePlugin.success('新建成功'); MessagePlugin.success('新建成功');
} }
}; };
const beforeUpload = (file) => { const beforeUpload = (file: UploadFile) => {
if (!/\.(pdf)$/.test(file.name)) { if (!/\.(pdf)$/.test(file.name)) {
MessagePlugin.warning('请上传pdf文件'); MessagePlugin.warning('请上传pdf文件');
return false; return false;
@ -190,11 +191,11 @@ const beforeUpload = (file) => {
} }
return true; return true;
}; };
const handleFail = ({ file }) => { const handleFail = (options: UploadFailContext) => {
MessagePlugin.error(`文件 ${file.name} 上传失败`); MessagePlugin.error(`文件 ${options.file.name} 上传失败`);
}; };
// error url / // error url /
const formatResponse = (res) => { const formatResponse = (res: any) => {
return { ...res, error: '上传失败,请重试', url: res.url }; return { ...res, error: '上传失败,请重试', url: res.url };
}; };
</script> </script>

View File

@ -29,7 +29,7 @@
:data="formData1" :data="formData1"
:rules="FORM_RULES" :rules="FORM_RULES"
label-align="right" label-align="right"
@submit="(result) => onSubmit(result, 1)" @submit="(result: SubmitContext) => onSubmit(result, 1)"
> >
<t-form-item label="合同名称" name="name"> <t-form-item label="合同名称" name="name">
<t-select v-model="formData1.name" :style="{ width: '480px' }" class="demo-select-base" clearable> <t-select v-model="formData1.name" :style="{ width: '480px' }" class="demo-select-base" clearable>
@ -59,7 +59,7 @@
:rules="FORM_RULES" :rules="FORM_RULES"
label-align="left" label-align="left"
@reset="onReset(0)" @reset="onReset(0)"
@submit="(result) => onSubmit(result, 2)" @submit="(result: SubmitContext) => onSubmit(result, 2)"
> >
<t-form-item label="发票抬头" name="title"> <t-form-item label="发票抬头" name="title">
<t-input v-model="formData2.title" :style="{ width: '480px' }" placeholder="请输入发票抬头" /> <t-input v-model="formData2.title" :style="{ width: '480px' }" placeholder="请输入发票抬头" />
@ -96,7 +96,7 @@
:rules="FORM_RULES" :rules="FORM_RULES"
label-align="left" label-align="left"
@reset="onReset(1)" @reset="onReset(1)"
@submit="(result) => onSubmit(result, 6)" @submit="(result: SubmitContext) => onSubmit(result, 6)"
> >
<t-form-item label="收货人" name="consignee"> <t-form-item label="收货人" name="consignee">
<t-input v-model="formData3.consignee" :style="{ width: '480px' }" placeholder="请输入收货人" /> <t-input v-model="formData3.consignee" :style="{ width: '480px' }" placeholder="请输入收货人" />
@ -147,7 +147,7 @@ export default {
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { Data, SubmitContext } from 'tdesign-vue-next'; import { SubmitContext } from 'tdesign-vue-next';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
@ -179,7 +179,7 @@ const amount = computed(() => {
return '--'; return '--';
}); });
const onSubmit = (result: SubmitContext<Data>, val: number) => { const onSubmit = (result: SubmitContext, val: number) => {
if (result.validateResult === true) { if (result.validateResult === true) {
activeForm.value = val; activeForm.value = val;
} }

View File

@ -160,10 +160,10 @@ const rowKey = 'index';
const rehandleSelectChange = (val: number[]) => { const rehandleSelectChange = (val: number[]) => {
selectedRowKeys.value = val; selectedRowKeys.value = val;
}; };
const rehandlePageChange = (curr, pageInfo) => { const rehandlePageChange = (curr: unknown, pageInfo: unknown) => {
console.log('分页变化', curr, pageInfo); console.log('分页变化', curr, pageInfo);
}; };
const rehandleChange = (changeParams, triggerAndData) => { const rehandleChange = (changeParams: unknown, triggerAndData: unknown) => {
console.log('统一Change', changeParams, triggerAndData); console.log('统一Change', changeParams, triggerAndData);
}; };
const handleClickDetail = () => { const handleClickDetail = () => {

View File

@ -73,15 +73,26 @@ import { MessagePlugin } from 'tdesign-vue-next';
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import { getCardList } from '@/api/list'; import { getCardList } from '@/api/list';
import type { CardProductType } from '@/components/product-card/index.vue';
import ProductCard from '@/components/product-card/index.vue'; import ProductCard from '@/components/product-card/index.vue';
import DialogForm from './components/DialogForm.vue'; import DialogForm from './components/DialogForm.vue';
const INITIAL_DATA = { interface FormData {
name: string;
status: string;
description: string;
type: number;
mark: string;
amount: number;
[key: string]: unknown;
}
const INITIAL_DATA: FormData = {
name: '', name: '',
status: '', status: '',
description: '', description: '',
type: '', type: 0,
mark: '', mark: '',
amount: 0, amount: 0,
}; };
@ -127,7 +138,7 @@ const onPageSizeChange = (size: number) => {
const onCurrentChange = (current: number) => { const onCurrentChange = (current: number) => {
pagination.value.current = current; pagination.value.current = current;
}; };
const handleDeleteItem = (product) => { const handleDeleteItem = (product: CardProductType) => {
confirmVisible.value = true; confirmVisible.value = true;
deleteProduct.value = product; deleteProduct.value = product;
}; };
@ -141,9 +152,16 @@ const onCancel = () => {
deleteProduct.value = undefined; deleteProduct.value = undefined;
formData.value = { ...INITIAL_DATA }; formData.value = { ...INITIAL_DATA };
}; };
const handleManageProduct = (product) => { const handleManageProduct = (product: CardProductType) => {
formDialogVisible.value = true; formDialogVisible.value = true;
formData.value = { ...product, status: product?.isSetup ? '1' : '0' }; formData.value = {
name: product.name,
status: product?.isSetup ? '1' : '0',
description: product.description,
type: product.type,
mark: '',
amount: 0,
};
}; };
</script> </script>

View File

@ -122,7 +122,7 @@ import { useSettingStore } from '@/store';
const store = useSettingStore(); const store = useSettingStore();
const COLUMNS: PrimaryTableCol<TableRowData>[] = [ const COLUMNS: PrimaryTableCol[] = [
{ {
title: '合同名称', title: '合同名称',
fixed: 'left', fixed: 'left',
@ -167,8 +167,8 @@ const COLUMNS: PrimaryTableCol<TableRowData>[] = [
const searchForm = { const searchForm = {
name: '', name: '',
no: undefined, no: '',
status: undefined, status: typeof CONTRACT_STATUS,
type: '', type: '',
}; };
@ -233,24 +233,24 @@ onMounted(() => {
fetchData(); fetchData();
}); });
const handleClickDelete = ({ row }) => { const handleClickDelete = (slot: { row: { rowIndex: number } }) => {
deleteIdx.value = row.rowIndex; deleteIdx.value = slot.row.rowIndex;
confirmVisible.value = true; confirmVisible.value = true;
}; };
const onReset = (val) => { const onReset = (val: unknown) => {
console.log(val); console.log(val);
}; };
const onSubmit = (val) => { const onSubmit = (val: unknown) => {
console.log(val); console.log(val);
}; };
const rehandlePageChange = (pageInfo: PageInfo, newDataSource: TableRowData[]) => { const rehandlePageChange = (pageInfo: PageInfo, newDataSource: TableRowData[]) => {
console.log('分页变化', pageInfo, newDataSource); console.log('分页变化', pageInfo, newDataSource);
}; };
const rehandleChange = (changeParams, triggerAndData) => { const rehandleChange = (changeParams: unknown, triggerAndData: unknown) => {
console.log('统一Change', changeParams, triggerAndData); console.log('统一Change', changeParams, triggerAndData);
}; };
const rehandleClickOp = ({ text, row }) => { const rehandleClickOp = (ctx: unknown) => {
console.log(text, row); console.log(ctx);
}; };
const headerAffixedTop = computed( const headerAffixedTop = computed(

View File

@ -24,6 +24,7 @@ export default {
<script setup lang="ts"> <script setup lang="ts">
import { SearchIcon } from 'tdesign-icons-vue-next'; import { SearchIcon } from 'tdesign-icons-vue-next';
import type { TreeNodeModel } from 'tdesign-vue-next';
import { ref } from 'vue'; import { ref } from 'vue';
import CommonTable from '../components/CommonTable.vue'; import CommonTable from '../components/CommonTable.vue';
@ -35,9 +36,8 @@ const filterText = ref();
const expanded = ['0', '0-0', '0-1', '0-2', '0-3', '0-4']; const expanded = ['0', '0-0', '0-1', '0-2', '0-3', '0-4'];
const onInput = () => { const onInput = () => {
filterByText.value = (node) => { filterByText.value = (node: TreeNodeModel) => {
const rs = node.label.indexOf(filterText.value) >= 0; return node.label.indexOf(filterText.value) >= 0;
return rs;
}; };
}; };
</script> </script>

View File

@ -80,7 +80,7 @@
<script setup lang="ts"> <script setup lang="ts">
import QrcodeVue from 'qrcode.vue'; import QrcodeVue from 'qrcode.vue';
import type { FormInstanceFunctions, FormRule } from 'tdesign-vue-next'; import type { FormInstanceFunctions, FormRule, SubmitContext } from 'tdesign-vue-next';
import { MessagePlugin } from 'tdesign-vue-next'; import { MessagePlugin } from 'tdesign-vue-next';
import { ref } from 'vue'; import { ref } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
@ -131,8 +131,8 @@ const sendCode = () => {
}); });
}; };
const onSubmit = async ({ validateResult }) => { const onSubmit = async (ctx: SubmitContext) => {
if (validateResult === true) { if (ctx.validateResult === true) {
try { try {
await userStore.login(formData.value); await userStore.login(formData.value);

View File

@ -71,7 +71,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { FormRule, MessagePlugin } from 'tdesign-vue-next'; import type { FormRule, SubmitContext } from 'tdesign-vue-next';
import { MessagePlugin } from 'tdesign-vue-next';
import { ref } from 'vue'; import { ref } from 'vue';
import { useCounter } from '@/hooks'; import { useCounter } from '@/hooks';
@ -105,8 +106,8 @@ const [countDown, handleCounter] = useCounter();
const emit = defineEmits(['registerSuccess']); const emit = defineEmits(['registerSuccess']);
const onSubmit = ({ validateResult }) => { const onSubmit = (ctx: SubmitContext) => {
if (validateResult === true) { if (ctx.validateResult === true) {
if (!formData.value.checked) { if (!formData.value.checked) {
MessagePlugin.error('请同意TDesign服务协议和TDesign 隐私声明'); MessagePlugin.error('请同意TDesign服务协议和TDesign 隐私声明');
return; return;
@ -116,7 +117,7 @@ const onSubmit = ({ validateResult }) => {
} }
}; };
const switchType = (val) => { const switchType = (val: string) => {
form.value.reset(); form.value.reset();
type.value = val; type.value = val;
}; };

View File

@ -1,4 +1,10 @@
export const USER_INFO_LIST = [ export interface UserInfoListType {
title: string;
content: string;
span?: number;
}
export const USER_INFO_LIST: Array<UserInfoListType> = [
{ {
title: '手机', title: '手机',
content: '+86 13923734567', content: '+86 13923734567',

View File

@ -16,7 +16,7 @@
</t-button> </t-button>
</template> </template>
<t-row class="content" justify="space-between"> <t-row class="content" justify="space-between">
<t-col v-for="(item, index) in USER_INFO_LIST" :key="index" class="contract" :span="item.span || 3"> <t-col v-for="(item, index) in USER_INFO_LIST" :key="index" class="contract" :span="item.span ?? 3">
<div class="contract-title"> <div class="contract-title">
{{ item.title }} {{ item.title }}
</div> </div>
@ -98,6 +98,7 @@ import { LineChart } from 'echarts/charts';
import { GridComponent, LegendComponent, TooltipComponent } from 'echarts/components'; import { GridComponent, LegendComponent, TooltipComponent } from 'echarts/components';
import * as echarts from 'echarts/core'; import * as echarts from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers'; import { CanvasRenderer } from 'echarts/renderers';
import type { DateRangeValue } from 'tdesign-vue-next';
import { computed, nextTick, onMounted, onUnmounted, watch } from 'vue'; import { computed, nextTick, onMounted, onUnmounted, watch } from 'vue';
import ProductAIcon from '@/assets/assets-product-1.svg'; import ProductAIcon from '@/assets/assets-product-1.svg';
@ -118,8 +119,13 @@ let lineChart: echarts.ECharts;
const store = useSettingStore(); const store = useSettingStore();
const chartColors = computed(() => store.chartColors); const chartColors = computed(() => store.chartColors);
const onLineChange = (value) => { const onLineChange = (value: DateRangeValue) => {
lineChart.setOption(getFolderLineDataSet(value)); lineChart.setOption(
getFolderLineDataSet({
dateTime: value as string[],
...chartColors.value,
}),
);
}; };
const initChart = () => { const initChart = () => {
@ -154,7 +160,7 @@ onUnmounted(() => {
window.removeEventListener('resize', updateContainer); window.removeEventListener('resize', updateContainer);
}); });
const getIcon = (type) => { const getIcon = (type: string) => {
switch (type) { switch (type) {
case 'a': case 'a':
return ProductAIcon; return ProductAIcon;

View File

@ -4,9 +4,8 @@ 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, useUserStore } from '@/store';
import { PAGE_NOT_FOUND_ROUTE } from '@/utils/route/constant'; import { PAGE_NOT_FOUND_ROUTE } from '@/utils/route/constant';
NProgress.configure({ showSpinner: false }); NProgress.configure({ showSpinner: false });
@ -17,9 +16,9 @@ router.beforeEach(async (to, from, next) => {
const permissionStore = getPermissionStore(); const permissionStore = getPermissionStore();
const { whiteListRouters } = permissionStore; const { whiteListRouters } = permissionStore;
const userStore = getUserStore(); const userStore = useUserStore();
if (userStore[TOKEN_NAME]) { if (userStore.token) {
if (to.path === '/login') { if (to.path === '/login') {
next(); next();
return; return;
@ -73,7 +72,7 @@ router.beforeEach(async (to, from, next) => {
router.afterEach((to) => { router.afterEach((to) => {
if (to.path === '/login') { if (to.path === '/login') {
const userStore = getUserStore(); const userStore = useUserStore();
const permissionStore = getPermissionStore(); const permissionStore = getPermissionStore();
userStore.logout(); userStore.logout();

View File

@ -40,7 +40,7 @@ export function mapModuleRouterList(modules: Record<string, unknown>): Array<Rou
} }
export const getRoutesExpanded = () => { export const getRoutesExpanded = () => {
const expandedRoutes = []; const expandedRoutes: Array<string> = [];
fixedRouterList.forEach((item) => { fixedRouterList.forEach((item) => {
if (item.meta && item.meta.expanded) { if (item.meta && item.meta.expanded) {

View File

@ -8,10 +8,10 @@ import router, { allRoutes } from '@/router';
import { store } from '@/store'; import { store } from '@/store';
function filterPermissionsRouters(routes: Array<RouteRecordRaw>, roles: Array<unknown>) { function filterPermissionsRouters(routes: Array<RouteRecordRaw>, roles: Array<unknown>) {
const res = []; const res: Array<RouteRecordRaw> = [];
const removeRoutes = []; const removeRoutes: Array<RouteRecordRaw> = [];
routes.forEach((route) => { routes.forEach((route) => {
const children = []; const children: Array<RouteRecordRaw> = [];
route.children?.forEach((childRouter) => { route.children?.forEach((childRouter) => {
const roleCode = childRouter.meta?.roleCode || childRouter.name; const roleCode = childRouter.meta?.roleCode || childRouter.name;
if (roles.indexOf(roleCode) !== -1) { if (roles.indexOf(roleCode) !== -1) {
@ -38,7 +38,7 @@ export const usePermissionStore = defineStore('permission', {
async initRoutes(roles: Array<unknown>) { async initRoutes(roles: Array<unknown>) {
let accessedRouters = []; let accessedRouters = [];
let removeRoutes = []; let removeRoutes: Array<RouteRecordRaw> = [];
// special token // special token
if (roles.includes('all')) { if (roles.includes('all')) {
accessedRouters = allRoutes; accessedRouters = allRoutes;

View File

@ -15,6 +15,7 @@ const state = {
}; };
export type TState = typeof state; export type TState = typeof state;
export type TStateKey = keyof typeof state;
export const useSettingStore = defineStore('setting', { export const useSettingStore = defineStore('setting', {
state: () => state, state: () => state,
@ -74,8 +75,8 @@ export const useSettingStore = defineStore('setting', {
}, },
updateConfig(payload: Partial<TState>) { updateConfig(payload: Partial<TState>) {
for (const key in payload) { for (const key in payload) {
if (payload[key] !== undefined) { if (payload[key as TStateKey] !== undefined) {
this[key] = payload[key]; this[key] = payload[key as TStateKey];
} }
if (key === 'mode') { if (key === 'mode') {
this.changeMode(payload[key]); this.changeMode(payload[key]);

View File

@ -36,7 +36,8 @@ export const useTabsRouterStore = defineStore('tabsRouter', {
}, },
// 处理新增 // 处理新增
appendTabRouterList(newRoute: TRouterInfo) { appendTabRouterList(newRoute: TRouterInfo) {
const needAlive = !ignoreCacheRoutes.includes(newRoute.name as string); // 不要将判断条件newRoute.meta.keepAlive !== false修改为newRoute.meta.keepAlivestarter默认开启保活所以meta.keepAlive未定义时也需要进行保活只有显式说明false才禁用保活。
const needAlive = !ignoreCacheRoutes.includes(newRoute.name as string) && newRoute.meta?.keepAlive !== false;
if (!this.tabRouters.find((route: TRouterInfo) => route.path === newRoute.path)) { if (!this.tabRouters.find((route: TRouterInfo) => route.path === newRoute.path)) {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
this.tabRouterList = this.tabRouterList.concat({ ...newRoute, isAlive: needAlive }); this.tabRouterList = this.tabRouterList.concat({ ...newRoute, isAlive: needAlive });
@ -50,7 +51,7 @@ export const useTabsRouterStore = defineStore('tabsRouter', {
// 处理关闭右侧 // 处理关闭右侧
subtractTabRouterBehind(newRoute: TRouterInfo) { subtractTabRouterBehind(newRoute: TRouterInfo) {
const { routeIdx } = newRoute; const { routeIdx } = newRoute;
const homeIdx: number = this.tabRouters.findIndex((route) => route.isHome); const homeIdx: number = this.tabRouters.findIndex((route: TRouterInfo) => route.isHome);
let tabRouterList: Array<TRouterInfo> = this.tabRouterList.slice(0, routeIdx + 1); let tabRouterList: Array<TRouterInfo> = this.tabRouterList.slice(0, routeIdx + 1);
if (routeIdx < homeIdx) { if (routeIdx < homeIdx) {
tabRouterList = tabRouterList.concat(homeRoute); tabRouterList = tabRouterList.concat(homeRoute);
@ -60,7 +61,7 @@ export const useTabsRouterStore = defineStore('tabsRouter', {
// 处理关闭左侧 // 处理关闭左侧
subtractTabRouterAhead(newRoute: TRouterInfo) { subtractTabRouterAhead(newRoute: TRouterInfo) {
const { routeIdx } = newRoute; const { routeIdx } = newRoute;
const homeIdx: number = this.tabRouters.findIndex((route) => route.isHome); const homeIdx: number = this.tabRouters.findIndex((route: TRouterInfo) => route.isHome);
let tabRouterList: Array<TRouterInfo> = this.tabRouterList.slice(routeIdx); let tabRouterList: Array<TRouterInfo> = this.tabRouterList.slice(routeIdx);
if (routeIdx > homeIdx) { if (routeIdx > homeIdx) {
tabRouterList = homeRoute.concat(tabRouterList); tabRouterList = homeRoute.concat(tabRouterList);
@ -70,7 +71,7 @@ export const useTabsRouterStore = defineStore('tabsRouter', {
// 处理关闭其他 // 处理关闭其他
subtractTabRouterOther(newRoute: TRouterInfo) { subtractTabRouterOther(newRoute: TRouterInfo) {
const { routeIdx } = newRoute; const { routeIdx } = newRoute;
const homeIdx: number = this.tabRouters.findIndex((route) => route.isHome); const homeIdx: number = this.tabRouters.findIndex((route: TRouterInfo) => route.isHome);
this.tabRouterList = routeIdx === homeIdx ? homeRoute : homeRoute.concat([this.tabRouterList?.[routeIdx]]); this.tabRouterList = routeIdx === homeIdx ? homeRoute : homeRoute.concat([this.tabRouterList?.[routeIdx]]);
}, },
removeTabRouterList() { removeTabRouterList() {

View File

@ -1,16 +1,16 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { TOKEN_NAME } from '@/config/global'; import { usePermissionStore } from '@/store';
import { store, usePermissionStore } from '@/store'; import type { UserInfo } from '@/types/interface';
const InitUserInfo = { const InitUserInfo: UserInfo = {
name: '', // 用户名,用于展示在页面右上角头像处 name: '', // 用户名,用于展示在页面右上角头像处
roles: [], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用 roles: [], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用
}; };
export const useUserStore = defineStore('user', { export const useUserStore = defineStore('user', {
state: () => ({ state: () => ({
[TOKEN_NAME]: 'main_token', // 默认token不走权限 token: 'main_token', // 默认token不走权限
userInfo: { ...InitUserInfo }, userInfo: { ...InitUserInfo },
}), }),
getters: { getters: {
@ -49,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.setToken(res.data); this.token = res.data;
} else { } else {
throw res; throw res;
} }
@ -67,20 +67,14 @@ 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_NAME]); const res = await mockRemoteUserInfo(this.token);
this.userInfo = res; this.userInfo = res;
}, },
async logout() { async logout() {
this.removeToken(); this.token = '';
this.userInfo = { ...InitUserInfo }; this.userInfo = { ...InitUserInfo };
}, },
async removeToken() {
this.setToken('');
},
async setToken(token: string) {
this[TOKEN_NAME] = token;
},
}, },
persist: { persist: {
afterRestore: () => { afterRestore: () => {
@ -88,10 +82,6 @@ export const useUserStore = defineStore('user', {
permissionStore.initRoutes(); permissionStore.initRoutes();
}, },
key: 'user', key: 'user',
paths: [TOKEN_NAME], paths: ['token'],
}, },
}); });
export function getUserStore() {
return useUserStore(store);
}

View File

@ -1,3 +1,4 @@
import type { TabValue } from 'tdesign-vue-next';
import { LocationQueryRaw, RouteRecordName } from 'vue-router'; import { LocationQueryRaw, RouteRecordName } from 'vue-router';
import STYLE_CONFIG from '@/config/style'; import STYLE_CONFIG from '@/config/style';
@ -26,6 +27,11 @@ export type CommonObjType = {
[key: string]: string | number; [key: string]: string | number;
}; };
export interface UserInfo {
name: string;
roles: string[];
}
export interface NotificationItem { export interface NotificationItem {
id: string; id: string;
content: string; content: string;
@ -51,3 +57,9 @@ export interface TTabRouterType {
isRefreshing: boolean; isRefreshing: boolean;
tabRouterList: Array<TRouterInfo>; tabRouterList: Array<TRouterInfo>;
} }
export interface TTabRemoveOptions {
value: TabValue;
index: number;
e: MouseEvent;
}

View File

@ -3,9 +3,8 @@ import type { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
import isString from 'lodash/isString'; import isString from 'lodash/isString';
import merge from 'lodash/merge'; import merge from 'lodash/merge';
import { TOKEN_NAME } from '@/config/global';
import { ContentTypeEnum } from '@/constants'; import { ContentTypeEnum } from '@/constants';
import { getUserStore } from '@/store'; import { useUserStore } from '@/store';
import { VAxios } from './Axios'; import { VAxios } from './Axios';
import type { AxiosTransform, CreateAxiosOptions } from './AxiosTransform'; import type { AxiosTransform, CreateAxiosOptions } from './AxiosTransform';
@ -114,8 +113,8 @@ const transform: AxiosTransform = {
// 请求拦截器处理 // 请求拦截器处理
requestInterceptors: (config, options) => { requestInterceptors: (config, options) => {
// 请求之前处理config // 请求之前处理config
const userStore = getUserStore(); const userStore = useUserStore();
const token = userStore[TOKEN_NAME]; const { token } = userStore;
if (token && (config as Recordable)?.requestOptions?.withToken !== false) { if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
// jwt token // jwt token

View File

@ -44,7 +44,7 @@ export function formatRequestDate(params: Recordable) {
} }
// 将对象转为Url参数 // 将对象转为Url参数
export function setObjToUrlParams(baseUrl: string, obj: object): string { export function setObjToUrlParams(baseUrl: string, obj: { [index: string]: any }): string {
let parameters = ''; let parameters = '';
for (const key in obj) { for (const key in obj) {
parameters += `${key}=${encodeURIComponent(obj[key])}&`; parameters += `${key}=${encodeURIComponent(obj[key])}&`;

View File

@ -15,7 +15,8 @@
"baseUrl": "./", "baseUrl": "./",
"paths": { "paths": {
"@/*": ["src/*"] "@/*": ["src/*"]
} },
"noImplicitAny": true
}, },
"include": [ "include": [
"**/*.ts", "**/*.ts",