mirror of
https://github.com/Tencent/tdesign-vue-next-starter.git
synced 2024-11-10 10:38:23 +08:00
docs: init
This commit is contained in:
parent
0ddeb3f3e7
commit
9fba04f8a5
48
.eslintrc
Normal file
48
.eslintrc
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"eslint-config-airbnb-base",
|
||||||
|
"plugin:vue/vue3-recommended",
|
||||||
|
"plugin:prettier/recommended"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true,
|
||||||
|
"jest": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"vue",
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"sourceType": "module",
|
||||||
|
"allowImportExportEverywhere": true,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"import/extensions": [
|
||||||
|
".js",
|
||||||
|
".jsx",
|
||||||
|
".ts",
|
||||||
|
".tsx"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"import/extensions": "off",
|
||||||
|
"import/no-unresolved": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"no-continue": "off",
|
||||||
|
"no-restricted-syntax": "off",
|
||||||
|
"guard-for-in": "off",
|
||||||
|
"import/no-extraneous-dependencies": "off",
|
||||||
|
"import/prefer-default-export": "off",
|
||||||
|
"no-plusplus": "off",
|
||||||
|
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||||
|
"no-param-reassign": "off",
|
||||||
|
"no-shadow": "off"
|
||||||
|
}
|
||||||
|
}
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,7 +16,9 @@ temp*
|
||||||
coverage
|
coverage
|
||||||
test-report.html
|
test-report.html
|
||||||
.idea/
|
.idea/
|
||||||
|
yarn.lock
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
package-lock.json
|
||||||
*.zip
|
*.zip
|
||||||
.history
|
.history
|
||||||
.stylelintcache
|
.stylelintcache
|
15
cache.dockerfile
Normal file
15
cache.dockerfile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# 选择一个 Base 镜像
|
||||||
|
FROM node:12
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /space
|
||||||
|
|
||||||
|
# 将 by 中的文件列表 COPY 过来
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 根据 COPY 过来的文件进行依赖的安装
|
||||||
|
RUN npm i
|
||||||
|
|
||||||
|
# 设置好需要的环境变量
|
||||||
|
ENV NODE_PATH=/space/node_modules
|
||||||
|
|
86
package.json
Normal file
86
package.json
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
{
|
||||||
|
"name": "tdesign-vue-next-starter",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"dev:mock": "vite --open --mode mock",
|
||||||
|
"dev": "vite --open --mode development",
|
||||||
|
"dev:linux": "vite --mode developmenet",
|
||||||
|
"build:test": "vite build --mode test",
|
||||||
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
|
"serve": "vite preview",
|
||||||
|
"lint": "eslint --ext .vue,.js,.jsx,.ts,.tsx ./ --max-warnings 0",
|
||||||
|
"lint:fix": "eslint --ext .vue,.js,jsx,.ts,.tsx ./ --max-warnings 0 --fix",
|
||||||
|
"stylelint": "stylelint src/**/*.{html,vue,sass,less}",
|
||||||
|
"stylelint:fix": "stylelint --cache --fix src/**/*.{html,vue,vss,sass,less}"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dayjs": "^1.10.6",
|
||||||
|
"echarts": "^5.2.1",
|
||||||
|
"nprogress": "^0.2.0",
|
||||||
|
"qrcode.vue": "^3.2.2",
|
||||||
|
"tdesign-vue-next": "^0.4.1",
|
||||||
|
"vue": "^3.1.5",
|
||||||
|
"vue-router": "^4.0.11",
|
||||||
|
"vue3-clipboard": "^1.0.0",
|
||||||
|
"vuex": "^4.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^13.1.0",
|
||||||
|
"@commitlint/config-conventional": "^13.1.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.29.3",
|
||||||
|
"@typescript-eslint/parser": "^4.29.3",
|
||||||
|
"@types/echarts": "^4.9.10",
|
||||||
|
"@vitejs/plugin-vue": "^1.3.0",
|
||||||
|
"@vitejs/plugin-vue-jsx": "^1.1.7",
|
||||||
|
"@vue/compiler-sfc": "^3.0.5",
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"commitizen": "^4.2.4",
|
||||||
|
"compressorjs": "^1.0.7",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-plugin-import": "^2.24.2",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"eslint-plugin-vue": "^7.16.0",
|
||||||
|
"http-proxy-agent": "^4.0.1",
|
||||||
|
"husky": "^7.0.4",
|
||||||
|
"less": "^4.1.1",
|
||||||
|
"mockjs": "^1.1.0",
|
||||||
|
"prettier": "^2.4.1",
|
||||||
|
"stylelint": "^13.13.1",
|
||||||
|
"stylelint-config-airbnb": "^0.0.0",
|
||||||
|
"stylelint-order": "^4.1.0",
|
||||||
|
"stylelint-scss": "^3.20.0",
|
||||||
|
"typescript": "^4.4.3",
|
||||||
|
"vite": "^2.4.4",
|
||||||
|
"vite-plugin-mock": "^2.9.6",
|
||||||
|
"vite-plugin-svg-sprite-component": "^1.0.10",
|
||||||
|
"vite-svg-loader": "^3.1.0",
|
||||||
|
"vue-clipboard3": "^1.0.1",
|
||||||
|
"vue-tsc": "^0.2.2",
|
||||||
|
"vuex-router-sync": "^5.0.0"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"commitizen": {
|
||||||
|
"path": "./node_modules/cz-conventional-changelog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged",
|
||||||
|
"prepare-commit-msg": "exec < /dev/tty && git cz --hook || true",
|
||||||
|
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,jsx,vue,ts,tsx}": [
|
||||||
|
"prettier --write",
|
||||||
|
"npm run lint:fix",
|
||||||
|
"git add"
|
||||||
|
],
|
||||||
|
"*.{html,vue,vss,sass,less}": [
|
||||||
|
"npm run stylelint:fix",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
18
src/main.ts
Normal file
18
src/main.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import TDesign from 'tdesign-vue-next/esm';
|
||||||
|
import VueClipboard from 'vue3-clipboard';
|
||||||
|
|
||||||
|
import App from './App.vue';
|
||||||
|
import { store } from './store';
|
||||||
|
import router from './router';
|
||||||
|
|
||||||
|
import '@/style/index.less';
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
|
||||||
|
app.use(TDesign);
|
||||||
|
app.use(store);
|
||||||
|
app.use(router);
|
||||||
|
app.use(VueClipboard);
|
||||||
|
|
||||||
|
app.mount('#app');
|
201
src/pages/dashboard/base/constants.ts
Normal file
201
src/pages/dashboard/base/constants.ts
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
import { TdBaseTableProps } from 'tdesign-vue-next';
|
||||||
|
|
||||||
|
interface DashboardPannel {
|
||||||
|
title: string;
|
||||||
|
number: string | number;
|
||||||
|
leftType: string;
|
||||||
|
upTrend?: string;
|
||||||
|
downTrend?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TendItem {
|
||||||
|
growUp?: number;
|
||||||
|
productName: string;
|
||||||
|
count: number;
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PANE_LIST: Array<DashboardPannel> = [
|
||||||
|
{
|
||||||
|
title: '总收入',
|
||||||
|
number: '¥ 28,425.00',
|
||||||
|
upTrend: '20.5%',
|
||||||
|
leftType: 'echarts-line',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '总退款',
|
||||||
|
number: '¥ 768.00',
|
||||||
|
downTrend: '20.5%',
|
||||||
|
leftType: 'echarts-bar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '活跃用户(个)',
|
||||||
|
number: '1126',
|
||||||
|
downTrend: '20.5%',
|
||||||
|
leftType: 'icon-usergroup',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '产生订单(个)',
|
||||||
|
number: 527,
|
||||||
|
downTrend: '20.5%',
|
||||||
|
leftType: 'icon-file-paste',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const SALE_TEND_LIST: Array<TendItem> = [
|
||||||
|
{
|
||||||
|
growUp: 1,
|
||||||
|
productName: '国家电网有限公司',
|
||||||
|
count: 7059,
|
||||||
|
date: '2021-09-01',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: -1,
|
||||||
|
productName: '深圳燃气集团股份有限公司',
|
||||||
|
count: 6437,
|
||||||
|
date: '2021-09-01',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: 4,
|
||||||
|
productName: '国家烟草专卖局',
|
||||||
|
count: 4221,
|
||||||
|
date: '2021-09-01',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: 3,
|
||||||
|
productName: '中国电信集团有限公司',
|
||||||
|
count: 3317,
|
||||||
|
date: '2021-09-01',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: -3,
|
||||||
|
productName: '中国移动通信集团有限公司',
|
||||||
|
count: 3015,
|
||||||
|
date: '2021-09-01',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: -3,
|
||||||
|
productName: '新余市办公用户采购项目',
|
||||||
|
count: 2015,
|
||||||
|
date: '2021-09-12',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const BUY_TEND_LIST: Array<TendItem> = [
|
||||||
|
{
|
||||||
|
growUp: 1,
|
||||||
|
productName: '腾讯科技(深圳)有限公司',
|
||||||
|
count: 3015,
|
||||||
|
date: '2021-09-01',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: -1,
|
||||||
|
productName: '大润发有限公司',
|
||||||
|
count: 2015,
|
||||||
|
date: '2021-09-01',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: 6,
|
||||||
|
productName: '四川海底捞股份有限公司',
|
||||||
|
count: 1815,
|
||||||
|
date: '2021-09-11',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: -3,
|
||||||
|
productName: '索尼(中国)有限公司',
|
||||||
|
count: 1015,
|
||||||
|
date: '2021-09-21',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: -4,
|
||||||
|
productName: '松下电器(中国)有限公司',
|
||||||
|
count: 445,
|
||||||
|
date: '2021-09-19',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
growUp: -3,
|
||||||
|
productName: '新余市办公用户采购项目',
|
||||||
|
count: 2015,
|
||||||
|
date: '2021-09-12',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const SALE_COLUMNS: TdBaseTableProps['columns'] = [
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'index',
|
||||||
|
title: '排名',
|
||||||
|
width: 64,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
colKey: 'productName',
|
||||||
|
title: '客户名称',
|
||||||
|
minWidth: '200',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'growUp',
|
||||||
|
width: 100,
|
||||||
|
title: '较上周',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'count',
|
||||||
|
title: '订单量',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'date',
|
||||||
|
width: 132,
|
||||||
|
title: '合同签订日期',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
width: 76,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const BUY_COLUMNS: TdBaseTableProps['columns'] = [
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'index',
|
||||||
|
title: '排名',
|
||||||
|
width: 64,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'left',
|
||||||
|
ellipsis: true,
|
||||||
|
colKey: 'productName',
|
||||||
|
title: '供应商名称',
|
||||||
|
minWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'growUp',
|
||||||
|
width: 100,
|
||||||
|
title: '较上周',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'count',
|
||||||
|
title: '订单量',
|
||||||
|
width: '100',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'date',
|
||||||
|
width: 132,
|
||||||
|
title: '合同签订日期',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
|
colKey: 'operation',
|
||||||
|
title: '操作',
|
||||||
|
width: 76,
|
||||||
|
},
|
||||||
|
];
|
317
src/pages/dashboard/base/index.vue
Normal file
317
src/pages/dashboard/base/index.vue
Normal file
|
@ -0,0 +1,317 @@
|
||||||
|
<template>
|
||||||
|
<div class="dashboard-panel">
|
||||||
|
<!-- 顶部 card -->
|
||||||
|
<t-row :gutter="16" class="row-container">
|
||||||
|
<t-col v-for="(item, index) in PANE_LIST" :key="item.title" :xs="6" :xl="3">
|
||||||
|
<card :describe="item.title" :style="{ height: '168px' }" :class="{ 'main-color': index == 0 }" size="small">
|
||||||
|
<div class="dashboard-item">
|
||||||
|
<div class="dashboard-item-top">
|
||||||
|
<span>{{ item.number }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="dashbord-item-left">
|
||||||
|
<div v-if="index === 0">
|
||||||
|
<div
|
||||||
|
id="moneyContainer"
|
||||||
|
class="dashboard-chart-container"
|
||||||
|
style="width: 96px; height: 40px; margin-top: 36px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="index === 1">
|
||||||
|
<div
|
||||||
|
id="refundContainer"
|
||||||
|
class="dashboard-chart-container"
|
||||||
|
style="width: 120px; height: 42px; margin-top: 24px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span v-else-if="index === 2">
|
||||||
|
<t-icon name="usergroup" />
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<t-icon name="file" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-item-bottom">
|
||||||
|
<div class="dashboard-item-block">
|
||||||
|
自从上周以来
|
||||||
|
<trend
|
||||||
|
class="dashboard-item-trend"
|
||||||
|
:type="item.upTrend ? 'up' : 'down'"
|
||||||
|
:is-reverse-color="index === 0"
|
||||||
|
:describe="item.upTrend || item.downTrend"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<t-icon name="chevron-right" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
|
||||||
|
<!-- 中部图表 -->
|
||||||
|
<t-row :gutter="16" class="row-container">
|
||||||
|
<t-col :xs="12" :xl="9">
|
||||||
|
<card title="统计数据">
|
||||||
|
<template #option>
|
||||||
|
<div class="dashboard-chart-title-left">(万元) {{ currentMonth }}</div>
|
||||||
|
<div class="dashboard-chart-title-container">
|
||||||
|
<t-date-picker
|
||||||
|
class="card-date-picker-slig"
|
||||||
|
theme="primary"
|
||||||
|
mode="date"
|
||||||
|
range
|
||||||
|
:default-value="LAST_7_DAYS"
|
||||||
|
@change="onCurrencyChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
id="monitorContainer"
|
||||||
|
ref="monitorContainer"
|
||||||
|
class="dashboard-chart-container"
|
||||||
|
style="width: 100%; height: 326px"
|
||||||
|
/>
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
<t-col :xs="12" :xl="3">
|
||||||
|
<card title="销售渠道">
|
||||||
|
<template #option>
|
||||||
|
<div class="dashboard-chart-title-left">{{ currentMonth }}</div>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
id="countContainer"
|
||||||
|
ref="countContainer"
|
||||||
|
style="width: 100%; height: 326px"
|
||||||
|
class="dashboard-chart-container"
|
||||||
|
/>
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
|
||||||
|
<!-- 列表排名 -->
|
||||||
|
<t-row :gutter="16" class="row-container">
|
||||||
|
<t-col :xs="12" :xl="6">
|
||||||
|
<card title="销售订单排名">
|
||||||
|
<template #option>
|
||||||
|
<t-radio-group default-value="dateVal">
|
||||||
|
<t-radio-button value="dateVal"> 月份 </t-radio-button>
|
||||||
|
<t-radio-button value="monthVal"> 季度 </t-radio-button>
|
||||||
|
</t-radio-group>
|
||||||
|
</template>
|
||||||
|
<t-table :data="SALE_TEND_LIST" :columns="SALE_COLUMNS" row-key="productName">
|
||||||
|
<template #index="{ rowIndex }">
|
||||||
|
<span :class="getRankClass(rowIndex)">
|
||||||
|
{{ rowIndex + 1 }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #growUp="{ row }">
|
||||||
|
<span>
|
||||||
|
<trend :type="row.growUp > 0 ? 'up' : 'down'" :describe="Math.abs(row.growUp)" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</t-table>
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
<t-col :xs="12" :xl="6">
|
||||||
|
<card title="采购订单排名">
|
||||||
|
<template #option>
|
||||||
|
<t-radio-group default-value="dateVal">
|
||||||
|
<t-radio-button value="dateVal"> 月份 </t-radio-button>
|
||||||
|
<t-radio-button value="monthVal"> 季度 </t-radio-button>
|
||||||
|
</t-radio-group>
|
||||||
|
</template>
|
||||||
|
<t-table :data="BUY_TEND_LIST" :columns="BUY_COLUMNS" row-key="productName">
|
||||||
|
<template #index="{ rowIndex }">
|
||||||
|
<span :class="getRankClass(rowIndex)">
|
||||||
|
{{ rowIndex + 1 }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #growUp="{ row }">
|
||||||
|
<trend :type="row.growUp > 0 ? 'up' : 'down'" :describe="Math.abs(row.growUp)" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #operation="slotProps">
|
||||||
|
<a class="link" @click="rehandleClickOp(slotProps)">操作</a>
|
||||||
|
</template>
|
||||||
|
</t-table>
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
|
||||||
|
<!-- 出入库概览 -->
|
||||||
|
<div class="overview-pannel">
|
||||||
|
<t-row>
|
||||||
|
<t-col :xs="12" :xl="9">
|
||||||
|
<card title="出入库概览">
|
||||||
|
<template #option>
|
||||||
|
<div class="dashboard-chart-title-left">(件)</div>
|
||||||
|
<div class="dashboard-chart-title-container">
|
||||||
|
<t-date-picker
|
||||||
|
theme="primary"
|
||||||
|
mode="date"
|
||||||
|
range
|
||||||
|
:default-value="LAST_7_DAYS"
|
||||||
|
@change="onWharehouseChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
id="stokeContainer"
|
||||||
|
ref="stokeContainer"
|
||||||
|
style="width: 100%; height: 351px"
|
||||||
|
class="dashboard-chart-container"
|
||||||
|
/>
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
<t-col :xs="12" :xl="3">
|
||||||
|
<div>
|
||||||
|
<card :style="{ margin: '0 0 -40px 0' }">
|
||||||
|
<template #option>
|
||||||
|
<t-button>导出数据</t-button>
|
||||||
|
</template>
|
||||||
|
</card>
|
||||||
|
<t-row>
|
||||||
|
<t-col :xs="6" :xl="12">
|
||||||
|
<card describe="本月出库总计(件)" :style="{ height: '168px' }" size="small">
|
||||||
|
<div class="dashboard-item">
|
||||||
|
<div class="dashboard-item-top">
|
||||||
|
<span>1726</span>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-item-bottom">
|
||||||
|
<div class="dashboard-item-block">
|
||||||
|
自从上周以来
|
||||||
|
<trend class="dashboard-item-trend" type="down" :is-reverse-color="false" describe="20.3%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
|
||||||
|
<t-col :xs="6" :xl="12">
|
||||||
|
<card describe="本月入库总计(件)" :style="{ height: '168px' }" size="small">
|
||||||
|
<div class="dashboard-item">
|
||||||
|
<div class="dashboard-item-top">
|
||||||
|
<span>226</span>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-item-bottom">
|
||||||
|
<div class="dashboard-item-block">
|
||||||
|
自从上周以来
|
||||||
|
<trend class="dashboard-item-trend" type="down" :is-reverse-color="false" describe="20.3%" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
</div>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, onMounted, watch, ref } 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 { LAST_7_DAYS } from '@/utils/date';
|
||||||
|
import { useChart } from '@/utils/hooks';
|
||||||
|
|
||||||
|
// 导入样式
|
||||||
|
import Trend from '@/components/trend/index.vue';
|
||||||
|
import Card from '@/components/card/index.vue';
|
||||||
|
import {
|
||||||
|
changeChartsTheme,
|
||||||
|
constructInitDataset,
|
||||||
|
getPieChartDataSet,
|
||||||
|
getLineChartDataSet,
|
||||||
|
constructInitDashbordDataset,
|
||||||
|
} from './index';
|
||||||
|
|
||||||
|
import { PANE_LIST, SALE_TEND_LIST, BUY_TEND_LIST, SALE_COLUMNS, BUY_COLUMNS } from './constants';
|
||||||
|
|
||||||
|
echarts.use([TooltipComponent, LegendComponent, PieChart, GridComponent, LineChart, BarChart, CanvasRenderer]);
|
||||||
|
|
||||||
|
const getThisMonth = (checkedValues?: string[]) => {
|
||||||
|
let date;
|
||||||
|
if (!checkedValues || checkedValues.length === 0) {
|
||||||
|
date = new Date();
|
||||||
|
return `${date.getFullYear()}-${date.getMonth() + 1}`;
|
||||||
|
}
|
||||||
|
date = new Date(checkedValues[0]);
|
||||||
|
const date2 = new Date(checkedValues[1]);
|
||||||
|
|
||||||
|
return `${date.getFullYear()}-${date.getMonth() + 1} 至 ${date2.getFullYear()}-${date2.getMonth() + 1}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'DashboardBase',
|
||||||
|
components: {
|
||||||
|
Card,
|
||||||
|
Trend,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const refundCharts = useChart('refundContainer');
|
||||||
|
const moneyCharts = useChart('moneyContainer');
|
||||||
|
const stokeCharts = useChart('stokeContainer');
|
||||||
|
const monitorChart = useChart('monitorContainer');
|
||||||
|
const countChart = useChart('countContainer');
|
||||||
|
const initCharts = () => {
|
||||||
|
refundCharts.value.setOption(constructInitDashbordDataset('bar'));
|
||||||
|
moneyCharts.value.setOption(constructInitDashbordDataset('line'));
|
||||||
|
stokeCharts.value.setOption(constructInitDataset(LAST_7_DAYS));
|
||||||
|
monitorChart.value.setOption(getLineChartDataSet());
|
||||||
|
countChart.value.setOption(getPieChartDataSet());
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentMonth = ref(getThisMonth());
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initCharts();
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
watch(
|
||||||
|
() => store.state.setting.brandTheme,
|
||||||
|
() => {
|
||||||
|
changeChartsTheme([
|
||||||
|
refundCharts.value,
|
||||||
|
moneyCharts.value,
|
||||||
|
stokeCharts.value,
|
||||||
|
monitorChart.value,
|
||||||
|
countChart.value,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentMonth,
|
||||||
|
LAST_7_DAYS,
|
||||||
|
PANE_LIST,
|
||||||
|
BUY_TEND_LIST,
|
||||||
|
SALE_TEND_LIST,
|
||||||
|
SALE_COLUMNS,
|
||||||
|
BUY_COLUMNS,
|
||||||
|
onCurrencyChange(checkedValues: string[]) {
|
||||||
|
currentMonth.value = getThisMonth(checkedValues);
|
||||||
|
monitorChart.value.setOption(getLineChartDataSet(checkedValues));
|
||||||
|
},
|
||||||
|
onWharehouseChange(checkedValues: string[]) {
|
||||||
|
stokeCharts.value.setOption(constructInitDataset(checkedValues));
|
||||||
|
},
|
||||||
|
rehandleClickOp(val: MouseEvent) {
|
||||||
|
console.log(val);
|
||||||
|
},
|
||||||
|
getRankClass(index: number) {
|
||||||
|
return ['dashbord-rank', { 'dashbord-rank__top': index < 3 }];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url('./index.less');
|
||||||
|
</style>
|
115
src/pages/dashboard/detail/index.vue
Normal file
115
src/pages/dashboard/detail/index.vue
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<template>
|
||||||
|
<div class="dashboard-panel-detail">
|
||||||
|
<card title="本月采购申请情况">
|
||||||
|
<t-row :gutter="16">
|
||||||
|
<t-col v-for="(item, index) in PANE_LIST_DATA" :key="index" :xs="6" :xl="3">
|
||||||
|
<div class="dashboard-detail-container-item">
|
||||||
|
<span>{{ item.title }}</span>
|
||||||
|
<h1>{{ item.count }}</h1>
|
||||||
|
<div class="dashboard-detail-container-item-text">
|
||||||
|
<span
|
||||||
|
>环比<trend :type="item.percent > 0 ? 'up' : 'down'" :describe="`${Math.abs(item.percent)}%`"
|
||||||
|
/></span>
|
||||||
|
<t-icon name="chevron-right" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
</card>
|
||||||
|
<t-row :gutter="16">
|
||||||
|
<t-col :xs="12" :xl="9">
|
||||||
|
<card title="采购商品申请趋势">
|
||||||
|
<template #option>
|
||||||
|
<div class="card-date-picker">
|
||||||
|
<t-date-picker
|
||||||
|
:default-value="LAST_7_DAYS"
|
||||||
|
theme="primary"
|
||||||
|
mode="date"
|
||||||
|
range
|
||||||
|
@change="onMaterialChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div id="lineContainer" style="width: 100%; height: 418px" />
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
<t-col :xs="12" :xl="3">
|
||||||
|
<product-card v-for="(item, index) in PRODUCT_LIST" :key="index" :product="item" />
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
<card title="采购商品满意度分布">
|
||||||
|
<template #option>
|
||||||
|
<t-date-picker
|
||||||
|
class="card-date-picker"
|
||||||
|
:default-value="LAST_7_DAYS"
|
||||||
|
theme="primary"
|
||||||
|
mode="date"
|
||||||
|
range
|
||||||
|
@change="onHappinesChange"
|
||||||
|
/>
|
||||||
|
<t-button class="card-date-button"> 导出数据 </t-button>
|
||||||
|
</template>
|
||||||
|
<div id="scatterContainer" style="width: 100%; height: 330px" />
|
||||||
|
</card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, onMounted, watch } from 'vue';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
|
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components';
|
||||||
|
import { LineChart, ScatterChart } from 'echarts/charts';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
import ProductCard from '@/pages/list/card/components/Card.vue';
|
||||||
|
|
||||||
|
import { changeChartsTheme, getFolderlineDataSet, getScattlerDataSet } from '../base/index';
|
||||||
|
import { PANE_LIST_DATA, PRODUCT_LIST } from './constants';
|
||||||
|
import { useChart } from '@/utils/hooks';
|
||||||
|
import { LAST_7_DAYS } from '@/utils/date';
|
||||||
|
|
||||||
|
import Trend from '@/components/trend/index.vue';
|
||||||
|
import Card from '@/components/card/index.vue';
|
||||||
|
|
||||||
|
echarts.use([GridComponent, LegendComponent, TooltipComponent, LineChart, ScatterChart, CanvasRenderer]);
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'DashboardDetail',
|
||||||
|
components: {
|
||||||
|
Card,
|
||||||
|
Trend,
|
||||||
|
ProductCard,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const lineChart = useChart('lineContainer');
|
||||||
|
const scatterChart = useChart('scatterContainer');
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
watch(
|
||||||
|
() => store.state.setting.brandTheme,
|
||||||
|
(val) => {
|
||||||
|
changeChartsTheme([lineChart.value, scatterChart.value], val);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
lineChart.value.setOption(getFolderlineDataSet());
|
||||||
|
scatterChart.value.setOption(getScattlerDataSet());
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
LAST_7_DAYS,
|
||||||
|
PRODUCT_LIST,
|
||||||
|
PANE_LIST_DATA,
|
||||||
|
onHappinesChange() {
|
||||||
|
scatterChart.value.setOption(getScattlerDataSet());
|
||||||
|
},
|
||||||
|
onMaterialChange(value) {
|
||||||
|
lineChart.value.setOption(getFolderlineDataSet(value));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url('./index.less');
|
||||||
|
</style>
|
186
src/pages/detail/deploy/index.vue
Normal file
186
src/pages/detail/deploy/index.vue
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
<template>
|
||||||
|
<t-row :gutter="16">
|
||||||
|
<t-col :span="6">
|
||||||
|
<card title="部署趋势">
|
||||||
|
<div class="deploy-panel-left">
|
||||||
|
<div id="monitorContainer" style="width: 100%; height: 265px" />
|
||||||
|
</div>
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
<t-col :span="6">
|
||||||
|
<card title="告警情况">
|
||||||
|
<template #option>
|
||||||
|
<t-radio-group default-value="dateVal" @change="onAlertChange">
|
||||||
|
<t-radio-button value="dateVal"> 本周 </t-radio-button>
|
||||||
|
<t-radio-button value="monthVal"> 本月 </t-radio-button>
|
||||||
|
</t-radio-group>
|
||||||
|
</template>
|
||||||
|
<div id="dataContainer" style="width: 100%; height: 265px" />
|
||||||
|
</card>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
|
||||||
|
<!-- 项目列表 -->
|
||||||
|
<card title="项目列表">
|
||||||
|
<t-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="data"
|
||||||
|
:pagination="pagination"
|
||||||
|
:hover="true"
|
||||||
|
row-key="index"
|
||||||
|
@sort-change="sortChange"
|
||||||
|
@change="rehandleChange"
|
||||||
|
>
|
||||||
|
<template #adminName="{ row }">
|
||||||
|
<span>
|
||||||
|
{{ row.adminName }}
|
||||||
|
<t-tag v-if="row.adminPhone" size="small" style="color: rgba(0, 0, 0, 0.4)">{{ row.adminPhone }}</t-tag>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #op="slotProps">
|
||||||
|
<a :class="PREFIX + '-link'" @click="listClick()">管理</a>
|
||||||
|
<a :class="PREFIX + '-link'" @click="deleteClickOp(slotProps)">删除</a>
|
||||||
|
</template>
|
||||||
|
<template #op-column>
|
||||||
|
<t-icon name="descending-order" />
|
||||||
|
</template>
|
||||||
|
</t-table>
|
||||||
|
</card>
|
||||||
|
|
||||||
|
<t-dialog v-model:visible="visible" header="基本信息" @confirm="onConfirm">
|
||||||
|
<template #body>
|
||||||
|
<div class="dialog-info-block">
|
||||||
|
<div class="dialog-info-block">
|
||||||
|
<div v-for="(item, index) in BASE_INFO_DATA" :key="index" class="info-item">
|
||||||
|
<h1>{{ item.name }}</h1>
|
||||||
|
<span
|
||||||
|
:class="{
|
||||||
|
['green']: item.type && item.type.value === 'green',
|
||||||
|
['blue']: item.type && item.type.value === 'blue',
|
||||||
|
}"
|
||||||
|
>{{ item.value }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</t-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, onMounted, onUnmounted, ref, watch } 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 { changeChartsTheme, getSmoothLineDataSet, get2ColBarChartDataSet } from '../../dashboard/base/index';
|
||||||
|
import { BASE_INFO_DATA, TABLE_COLUMNS } from './constants';
|
||||||
|
|
||||||
|
import { PREFIX } from '@/config/global';
|
||||||
|
import Card from '@/components/card/index.vue';
|
||||||
|
import { ResDataType } from '@/interface';
|
||||||
|
import request from '@/utils/request';
|
||||||
|
import { useChart } from '@/utils/hooks';
|
||||||
|
|
||||||
|
echarts.use([
|
||||||
|
TitleComponent,
|
||||||
|
ToolboxComponent,
|
||||||
|
TooltipComponent,
|
||||||
|
GridComponent,
|
||||||
|
LegendComponent,
|
||||||
|
BarChart,
|
||||||
|
LineChart,
|
||||||
|
CanvasRenderer,
|
||||||
|
]);
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'DetailDeploy',
|
||||||
|
components: { Card },
|
||||||
|
setup() {
|
||||||
|
const data = ref([]);
|
||||||
|
const pagination = ref({
|
||||||
|
defaultPageSize: 10,
|
||||||
|
total: 100,
|
||||||
|
defaultCurrent: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
try {
|
||||||
|
const res: ResDataType = await request.get('/api/get-project-list');
|
||||||
|
if (res.code === 0) {
|
||||||
|
const { list = [] } = res.data;
|
||||||
|
data.value = list;
|
||||||
|
pagination.value = {
|
||||||
|
...pagination.value,
|
||||||
|
total: list.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const visible = ref(false);
|
||||||
|
|
||||||
|
const monitorChart = useChart('monitorContainer');
|
||||||
|
const dataChart = useChart('dataContainer');
|
||||||
|
|
||||||
|
const intervalTimer = null;
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData();
|
||||||
|
dataChart.value.setOption(get2ColBarChartDataSet());
|
||||||
|
monitorChart.value.setOption(getSmoothLineDataSet());
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
monitorChart.value.setOption(getSmoothLineDataSet());
|
||||||
|
}, 3000);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(intervalTimer);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onAlertChange = () => {
|
||||||
|
dataChart.value.setOption(get2ColBarChartDataSet());
|
||||||
|
};
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
watch(
|
||||||
|
() => store.state.setting.brandTheme,
|
||||||
|
(val) => {
|
||||||
|
changeChartsTheme([monitorChart.value, dataChart.value], val);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
PREFIX,
|
||||||
|
BASE_INFO_DATA,
|
||||||
|
columns: TABLE_COLUMNS,
|
||||||
|
data,
|
||||||
|
pagination,
|
||||||
|
visible,
|
||||||
|
sortChange(val) {
|
||||||
|
console.log(val);
|
||||||
|
},
|
||||||
|
rehandleChange(changeParams, triggerAndData) {
|
||||||
|
console.log('统一Change', changeParams, triggerAndData);
|
||||||
|
},
|
||||||
|
listClick() {
|
||||||
|
visible.value = true;
|
||||||
|
},
|
||||||
|
onConfirm() {
|
||||||
|
visible.value = false;
|
||||||
|
},
|
||||||
|
deleteClickOp(e) {
|
||||||
|
data.value.splice(e.index, 1);
|
||||||
|
},
|
||||||
|
onAlertChange,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url('./index.less');
|
||||||
|
</style>
|
210
src/pages/form/base/index.vue
Normal file
210
src/pages/form/base/index.vue
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
<template>
|
||||||
|
<div :class="`${PREFIX}-panel form-basic-container`">
|
||||||
|
<div class="form-basic-item">
|
||||||
|
<div class="form-basic-container-title">合同信息</div>
|
||||||
|
<!-- 表单内容 -->
|
||||||
|
<t-form
|
||||||
|
ref="form"
|
||||||
|
class="base-form"
|
||||||
|
:data="formData"
|
||||||
|
:rules="FORM_RULES"
|
||||||
|
label-align="top"
|
||||||
|
:label-width="100"
|
||||||
|
@reset="onReset"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<!-- 合同名称,合同类型 -->
|
||||||
|
<t-row class="row-gap">
|
||||||
|
<t-col :flex="1">
|
||||||
|
<t-form-item label="合同名称" name="name">
|
||||||
|
<t-input v-model="formData.name" :style="{ width: '322px' }" placeholder="请输入内容" />
|
||||||
|
</t-form-item>
|
||||||
|
</t-col>
|
||||||
|
<t-col :flex="1">
|
||||||
|
<t-form-item label="合同类型" name="type" class="form-gap">
|
||||||
|
<t-select
|
||||||
|
v-model="formData.type"
|
||||||
|
:style="{ width: '322px' }"
|
||||||
|
placeholder="请选择类型"
|
||||||
|
class="demo-select-base"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<t-option v-for="(item, index) in TYPE_OPTIONS" :key="index" :value="item.value" :label="item.label">
|
||||||
|
{{ item.label }}
|
||||||
|
</t-option>
|
||||||
|
</t-select>
|
||||||
|
</t-form-item>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
|
||||||
|
<!-- 合同收付类型 -->
|
||||||
|
<t-row class="row-gap">
|
||||||
|
<t-col :flex="1">
|
||||||
|
<t-form-item label="合同收付类型" name="payment">
|
||||||
|
<t-radio-group v-model="formData.payment">
|
||||||
|
<t-radio value="1"> 收款 </t-radio>
|
||||||
|
<t-radio value="2"> 付款 </t-radio>
|
||||||
|
</t-radio-group>
|
||||||
|
<span class="span-item" />
|
||||||
|
<t-input placeholder="请输入金额" :style="{ width: '160px' }" />
|
||||||
|
</t-form-item>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
|
||||||
|
<!-- 甲方 / 乙方 -->
|
||||||
|
<t-row class="row-gap">
|
||||||
|
<t-col :flex="1">
|
||||||
|
<t-form-item label="甲方" name="partyA">
|
||||||
|
<t-select v-model="formData.partyA" :style="{ width: '322px' }" class="demo-select-base" clearable>
|
||||||
|
<t-option v-for="(item, index) in PARTY_A_OPTIONS" :key="index" :value="item.value" :label="item.label">
|
||||||
|
{{ item.label }}
|
||||||
|
</t-option>
|
||||||
|
</t-select>
|
||||||
|
</t-form-item>
|
||||||
|
</t-col>
|
||||||
|
<t-col :flex="1">
|
||||||
|
<t-form-item label="乙方" name="partyB" class="form-gap">
|
||||||
|
<t-select v-model="formData.partyB" :style="{ width: '322px' }" class="demo-select-base" clearable>
|
||||||
|
<t-option v-for="(item, index) in PARTY_B_OPTIONS" :key="index" :value="item.value" :label="item.label">
|
||||||
|
{{ item.label }}
|
||||||
|
</t-option>
|
||||||
|
</t-select>
|
||||||
|
</t-form-item>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
|
||||||
|
<t-row class="row-gap">
|
||||||
|
<t-col :flex="1">
|
||||||
|
<t-form-item label="合同签订日期" name="signDate">
|
||||||
|
<t-date-picker
|
||||||
|
v-model="formData.signDate"
|
||||||
|
:style="{ width: '322px' }"
|
||||||
|
theme="primary"
|
||||||
|
mode="date"
|
||||||
|
separator="/"
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
|
</t-col>
|
||||||
|
<t-col :flex="1">
|
||||||
|
<t-form-item label="合同生效日期" name="startDate" class="form-gap">
|
||||||
|
<t-date-picker
|
||||||
|
v-model="formData.startDate"
|
||||||
|
:style="{ width: '322px' }"
|
||||||
|
theme="primary"
|
||||||
|
mode="date"
|
||||||
|
separator="/"
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
<t-form-item label="合同结束日期" name="endDate">
|
||||||
|
<t-date-picker
|
||||||
|
v-model="formData.endDate"
|
||||||
|
:style="{ width: '322px' }"
|
||||||
|
theme="primary"
|
||||||
|
mode="date"
|
||||||
|
separator="/"
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
|
</t-form>
|
||||||
|
|
||||||
|
<div class="form-basic-container-title form-title-gap">其它信息</div>
|
||||||
|
<t-form
|
||||||
|
ref="form"
|
||||||
|
class="base-form"
|
||||||
|
:data="formData"
|
||||||
|
:rules="FORM_RULES"
|
||||||
|
label-align="top"
|
||||||
|
:label-width="100"
|
||||||
|
@reset="onReset"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<t-form-item label="备注" name="comment">
|
||||||
|
<t-textarea v-model="formData.comment" placeholder="请输入备注" />
|
||||||
|
</t-form-item>
|
||||||
|
</t-form>
|
||||||
|
<div class="form-cretifier">
|
||||||
|
<span class="form-cretifier-span">公证人</span>
|
||||||
|
<div class="form-cretifier-container">
|
||||||
|
<div class="form-cretifier-circle">D</div>
|
||||||
|
<div class="form-cretifier-circle form-cretifier-gap1">S</div>
|
||||||
|
<div class="form-cretifier-circle form-cretifier-gap2 form-cretifier-blure">+</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-submit-container">
|
||||||
|
<div class="form-submint-sub">
|
||||||
|
<div class="form-submit-left">
|
||||||
|
<t-upload
|
||||||
|
v-model="files"
|
||||||
|
action=""
|
||||||
|
placeholder="支持批量上传文件,文 件格式不限,最多只能上传 10 份文件"
|
||||||
|
:format-response="formatResponse"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
@fail="handleFail"
|
||||||
|
>
|
||||||
|
<t-button class="form-submit-upload-btn" variant="outline"> 上传合同文件 </t-button>
|
||||||
|
</t-upload>
|
||||||
|
<span class="form-submit-upload-span">请上传pdf文件,大小在60M以内</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-submit-right">
|
||||||
|
<t-button type="reset" class="form-submit-cancel" theme="default" variant="base"> 取消 </t-button>
|
||||||
|
<t-button theme="primary" class="form-submit-confirm" type="submit"> 提交 </t-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref } from 'vue';
|
||||||
|
import { MessagePlugin } from 'tdesign-vue-next';
|
||||||
|
import { PREFIX } from '@/config/global';
|
||||||
|
import { FORM_RULES, INITIAL_DATA, TYPE_OPTIONS, PARTY_A_OPTIONS, PARTY_B_OPTIONS } from './constants';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'FormBase',
|
||||||
|
setup() {
|
||||||
|
const formData = ref({ ...INITIAL_DATA });
|
||||||
|
const files = ref([]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
PREFIX,
|
||||||
|
TYPE_OPTIONS,
|
||||||
|
PARTY_A_OPTIONS,
|
||||||
|
PARTY_B_OPTIONS,
|
||||||
|
FORM_RULES,
|
||||||
|
formData,
|
||||||
|
files,
|
||||||
|
onReset() {
|
||||||
|
MessagePlugin.warning('取消新建');
|
||||||
|
},
|
||||||
|
onSubmit({ validateResult }) {
|
||||||
|
if (validateResult === true) {
|
||||||
|
MessagePlugin.success('新建成功');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeUpload(file) {
|
||||||
|
if (!/\.(pdf)$/.test(file.name)) {
|
||||||
|
MessagePlugin.warning('请上传pdf文件');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (file.size > 60 * 1024 * 1024) {
|
||||||
|
MessagePlugin.warning('上传文件不能大于60M');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
handleFail({ file }) {
|
||||||
|
MessagePlugin.error(`文件 ${file.name} 上传失败`);
|
||||||
|
},
|
||||||
|
// 用于格式化接口响应值,error 会被用于上传失败的提示文字;url 表示文件/图片地址
|
||||||
|
formatResponse(res) {
|
||||||
|
return { ...res, error: '上传失败,请重试', url: res.url };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url('./index.less');
|
||||||
|
</style>
|
199
src/pages/list/base/index.vue
Normal file
199
src/pages/list/base/index.vue
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
<template>
|
||||||
|
<div :class="`${PREFIX}-panel`">
|
||||||
|
<t-row :class="`${PREFIX}-operater-row`" justify="space-between">
|
||||||
|
<div>
|
||||||
|
<t-button @click="handleSetupContract"> 新建合同 </t-button>
|
||||||
|
<t-button variant="base" theme="default" :disabled="!selectedRowKeys.length"> 导出合同 </t-button>
|
||||||
|
<p v-if="!!selectedRowKeys.length" :class="`${PREFIX}-selected-count`">已选{{ selectedRowKeys.length }}项</p>
|
||||||
|
</div>
|
||||||
|
<div :class="`${PREFIX}-search-input`">
|
||||||
|
<t-input v-model="searchValue" placeholder="请输入你需要搜索的内容" clearable>
|
||||||
|
<template #suffix-icon>
|
||||||
|
<t-icon-search size="20px" />
|
||||||
|
</template>
|
||||||
|
</t-input>
|
||||||
|
</div>
|
||||||
|
</t-row>
|
||||||
|
<div class="table-container">
|
||||||
|
<t-table
|
||||||
|
:data="data"
|
||||||
|
:columns="COLUMNS"
|
||||||
|
:row-key="rowKey"
|
||||||
|
vertical-align="top"
|
||||||
|
:hover="true"
|
||||||
|
:pagination="pagination"
|
||||||
|
:selected-row-keys="selectedRowKeys"
|
||||||
|
:loading="dataLoading"
|
||||||
|
@page-change="rehandlePageChange"
|
||||||
|
@change="rehandleChange"
|
||||||
|
@select-change="rehandleSelectChange"
|
||||||
|
>
|
||||||
|
<template #status="{ row }">
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.FAIL" theme="danger" variant="light"> 审核失败 </t-tag>
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.AUDIT_PENDING" theme="warning" variant="light"> 待审核 </t-tag>
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.EXEC_PENDING" theme="warning" variant="light"> 待履行 </t-tag>
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.EXECUTING" theme="success" variant="light"> 履行中 </t-tag>
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.FINISH" theme="success" variant="light"> 已完成 </t-tag>
|
||||||
|
</template>
|
||||||
|
<template #contractType="{ row }">
|
||||||
|
<p v-if="row.contractType === CONTRACT_TYPES.MAIN">审核失败</p>
|
||||||
|
<p v-if="row.contractType === CONTRACT_TYPES.SUB">待审核</p>
|
||||||
|
<p v-if="row.contractType === CONTRACT_TYPES.SUPPLEMENT">待履行</p>
|
||||||
|
</template>
|
||||||
|
<template #paymentType="{ row }">
|
||||||
|
<p v-if="row.paymentType === CONTRACT_PAYMENT_TYPES.PAYMENT" class="payment-col">
|
||||||
|
付款<trend class="dashboard-item-trend" type="up" />
|
||||||
|
</p>
|
||||||
|
<p v-if="row.paymentType === CONTRACT_PAYMENT_TYPES.RECIPT" class="payment-col">
|
||||||
|
收款<trend class="dashboard-item-trend" type="down" />
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #op="slotProps">
|
||||||
|
<a :class="`${PREFIX}-link`" @click="handleClickDetail()">详情</a>
|
||||||
|
<a :class="`${PREFIX}-link`" @click="handleClickDelete(slotProps)">删除</a>
|
||||||
|
</template>
|
||||||
|
</t-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<t-dialog
|
||||||
|
v-model:visible="confirmVisible"
|
||||||
|
header="是否确认删除"
|
||||||
|
:body="confirmBody"
|
||||||
|
:on-cancel="onCancel"
|
||||||
|
@confirm="onConfirmDelete"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref, onMounted, computed } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import TIconSearch from 'tdesign-vue-next/lib/icon/search';
|
||||||
|
import { MessagePlugin } from 'tdesign-vue-next';
|
||||||
|
|
||||||
|
import { PREFIX } from '@/config/global';
|
||||||
|
import { CONTRACT_STATUS, CONTRACT_TYPES, CONTRACT_PAYMENT_TYPES } from '@/constants';
|
||||||
|
import Trend from '@/components/trend/index.vue';
|
||||||
|
import { ResDataType } from '@/interface';
|
||||||
|
import request from '@/utils/request';
|
||||||
|
|
||||||
|
import { COLUMNS } from './constants';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ListBaseCard',
|
||||||
|
components: {
|
||||||
|
TIconSearch,
|
||||||
|
Trend,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const data = ref([]);
|
||||||
|
const pagination = ref({
|
||||||
|
defaultPageSize: 20,
|
||||||
|
total: 100,
|
||||||
|
defaultCurrent: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchValue = ref('');
|
||||||
|
|
||||||
|
const dataLoading = ref(false);
|
||||||
|
const fetchData = async () => {
|
||||||
|
dataLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res: ResDataType = await request.get('/api/get-list');
|
||||||
|
if (res.code === 0) {
|
||||||
|
const { list = [] } = res.data;
|
||||||
|
data.value = list;
|
||||||
|
pagination.value = {
|
||||||
|
...pagination.value,
|
||||||
|
total: list.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
} finally {
|
||||||
|
dataLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteIdx = ref(-1);
|
||||||
|
const confirmBody = computed(() => {
|
||||||
|
if (deleteIdx.value > -1) {
|
||||||
|
const { no, name } = data.value[deleteIdx.value];
|
||||||
|
return `产品编号:${no}, 产品名称: ${name}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData();
|
||||||
|
});
|
||||||
|
|
||||||
|
const confirmVisible = ref(false);
|
||||||
|
|
||||||
|
const selectedRowKeys = ref([1, 2]);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const resetIdx = () => {
|
||||||
|
deleteIdx.value = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirmDelete = () => {
|
||||||
|
// 真实业务请发起请求
|
||||||
|
data.value.splice(deleteIdx.value - 1, 1);
|
||||||
|
pagination.value.total = data.value.length;
|
||||||
|
const selectedIdx = selectedRowKeys.value.indexOf(deleteIdx.value);
|
||||||
|
if (selectedIdx > -1) {
|
||||||
|
selectedRowKeys.value.splice(selectedIdx, 1);
|
||||||
|
}
|
||||||
|
confirmVisible.value = false;
|
||||||
|
MessagePlugin.success('删除成功');
|
||||||
|
resetIdx();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
resetIdx();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
CONTRACT_STATUS,
|
||||||
|
CONTRACT_TYPES,
|
||||||
|
CONTRACT_PAYMENT_TYPES,
|
||||||
|
COLUMNS,
|
||||||
|
PREFIX,
|
||||||
|
data,
|
||||||
|
searchValue,
|
||||||
|
dataLoading,
|
||||||
|
pagination,
|
||||||
|
confirmBody,
|
||||||
|
confirmVisible,
|
||||||
|
rowKey: 'index',
|
||||||
|
onConfirmDelete,
|
||||||
|
onCancel,
|
||||||
|
selectedRowKeys,
|
||||||
|
rehandleSelectChange(val: number[]) {
|
||||||
|
selectedRowKeys.value = val;
|
||||||
|
},
|
||||||
|
rehandlePageChange(curr, pageInfo) {
|
||||||
|
console.log('分页变化', curr, pageInfo);
|
||||||
|
},
|
||||||
|
rehandleChange(changeParams, triggerAndData) {
|
||||||
|
console.log('统一Change', changeParams, triggerAndData);
|
||||||
|
},
|
||||||
|
handleClickDetail() {
|
||||||
|
router.push('/detail/base');
|
||||||
|
},
|
||||||
|
handleSetupContract() {
|
||||||
|
router.push('/form/base');
|
||||||
|
},
|
||||||
|
handleClickDelete({ row }) {
|
||||||
|
deleteIdx.value = row.index;
|
||||||
|
confirmVisible.value = true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url('./index.less');
|
||||||
|
</style>
|
240
src/pages/list/card/components/Card.vue
Normal file
240
src/pages/list/card/components/Card.vue
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
<template>
|
||||||
|
<div :class="cardClass">
|
||||||
|
<div class="list-card-item_detail">
|
||||||
|
<t-row justify="space-between">
|
||||||
|
<div :class="cardLogoClass">
|
||||||
|
<t-icon-shop v-if="product.type === 1" />
|
||||||
|
<t-icon-calendar v-if="product.type === 2" />
|
||||||
|
<t-icon-service v-if="product.type === 3" />
|
||||||
|
<t-icon-user-avatar v-if="product.type === 4" />
|
||||||
|
<t-icon-laptop v-if="product.type === 5" />
|
||||||
|
</div>
|
||||||
|
<p :class="cardStatusClass">
|
||||||
|
{{ product.isSetup ? '已启用' : '已停用' }}
|
||||||
|
</p>
|
||||||
|
</t-row>
|
||||||
|
<p class="list-card-item_detail--name">
|
||||||
|
{{ product.name }}
|
||||||
|
</p>
|
||||||
|
<p class="list-card-item_detail--desc">
|
||||||
|
{{ product.description }}
|
||||||
|
</p>
|
||||||
|
<t-row justify="space-between" align="middle" :class="cardControlClass">
|
||||||
|
<div>
|
||||||
|
<t-button shape="circle" :disabled="!product.isSetup">
|
||||||
|
{{ typeMap[product.type - 1] }}
|
||||||
|
</t-button>
|
||||||
|
<t-button shape="circle" :disabled="!product.isSetup">
|
||||||
|
<t-icon-add />
|
||||||
|
</t-button>
|
||||||
|
</div>
|
||||||
|
<t-dropdown
|
||||||
|
:disabled="!product.isSetup"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
content: '管理',
|
||||||
|
value: 'manage',
|
||||||
|
onClick: () => handleClickManage(product),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: '删除',
|
||||||
|
value: 'delete',
|
||||||
|
onClick: () => handleClickDelete(product),
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<t-icon-more />
|
||||||
|
</t-dropdown>
|
||||||
|
</t-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType } from 'vue';
|
||||||
|
import TIconShop from 'tdesign-vue-next/lib/icon/shop';
|
||||||
|
import TIconCalendar from 'tdesign-vue-next/lib/icon/calendar';
|
||||||
|
import TIconService from 'tdesign-vue-next/lib/icon/service';
|
||||||
|
import TIconUserAvatar from 'tdesign-vue-next/lib/icon/user-avatar';
|
||||||
|
import TIconLaptop from 'tdesign-vue-next/lib/icon/laptop';
|
||||||
|
import TIconMore from 'tdesign-vue-next/lib/icon/more';
|
||||||
|
import TIconAdd from 'tdesign-vue-next/lib/icon/add';
|
||||||
|
|
||||||
|
export interface CardProductType {
|
||||||
|
type: number;
|
||||||
|
isSetup: boolean;
|
||||||
|
description: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ListCardComponent',
|
||||||
|
components: {
|
||||||
|
TIconShop,
|
||||||
|
TIconCalendar,
|
||||||
|
TIconService,
|
||||||
|
TIconUserAvatar,
|
||||||
|
TIconLaptop,
|
||||||
|
TIconMore,
|
||||||
|
TIconAdd,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
product: {
|
||||||
|
type: Object as PropType<CardProductType>,
|
||||||
|
default: () => {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['manage-product', 'delete-item'],
|
||||||
|
setup(props, ctx) {
|
||||||
|
const { emit } = ctx;
|
||||||
|
const cardClass = computed(() => [
|
||||||
|
'list-card-item',
|
||||||
|
{
|
||||||
|
'list-card-item__disabled': !props.product.isSetup,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const cardLogoClass = computed(() => [
|
||||||
|
'list-card-item_detail--logo',
|
||||||
|
{
|
||||||
|
'list-card-item_detail--logo__disabled': !props.product.isSetup,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const cardStatusClass = computed(() => [
|
||||||
|
'list-card-item_detail--status',
|
||||||
|
{
|
||||||
|
'list-card-item_detail--status__disabled': !props.product.isSetup,
|
||||||
|
'list-card-item_detail--status__setup': props.product.isSetup,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const cardControlClass = computed(() => [
|
||||||
|
'list-card-item_detail--control',
|
||||||
|
{
|
||||||
|
'list-card-item_detail--control__disabled': !props.product.isSetup,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
cardClass,
|
||||||
|
cardLogoClass,
|
||||||
|
cardStatusClass,
|
||||||
|
cardControlClass,
|
||||||
|
typeMap: ['A', 'B', 'C', 'D', 'E'],
|
||||||
|
handleClickManage(product) {
|
||||||
|
emit('manage-product', product);
|
||||||
|
},
|
||||||
|
handleClickDelete(product) {
|
||||||
|
emit('delete-item', product);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import '@/style/index';
|
||||||
|
|
||||||
|
.list-card-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: @border-radius;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
color: @text-color-secondary;
|
||||||
|
|
||||||
|
&_detail {
|
||||||
|
flex: 1;
|
||||||
|
background: @bg-color-container;
|
||||||
|
padding: 24px 32px;
|
||||||
|
min-height: 140px;
|
||||||
|
|
||||||
|
&--logo {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: @brand-color-1;
|
||||||
|
font-size: 32px;
|
||||||
|
color: @brand-color;
|
||||||
|
|
||||||
|
&__disabled {
|
||||||
|
color: @text-color-disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--name {
|
||||||
|
margin: 24px 0 8px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--status {
|
||||||
|
border-radius: @border-radius;
|
||||||
|
width: 52px;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
&__disabled {
|
||||||
|
background: @gray-color-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__setup {
|
||||||
|
background: @success-color-5;
|
||||||
|
color: @text-color-anti;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--desc {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--control {
|
||||||
|
> div:first-child {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> button:last-child {
|
||||||
|
position: absolute;
|
||||||
|
left: 18px;
|
||||||
|
background-color: @brand-color-2;
|
||||||
|
--ripple-color: @brand-color-2;
|
||||||
|
color: @brand-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__disabled {
|
||||||
|
> div:first-child {
|
||||||
|
> button:first-child {
|
||||||
|
background-color: @gray-color-6;
|
||||||
|
color: @text-color-anti;
|
||||||
|
}
|
||||||
|
|
||||||
|
> button:last-child {
|
||||||
|
background-color: @gray-color-2;
|
||||||
|
color: @text-color-disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__disabled {
|
||||||
|
color: @text-color-disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
121
src/pages/list/card/components/DialogForm.vue
Normal file
121
src/pages/list/card/components/DialogForm.vue
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<template>
|
||||||
|
<t-dialog v-model:visible="formVisible" header="新建产品" :width="680" :footer="false">
|
||||||
|
<template #body>
|
||||||
|
<!-- 表单内容 -->
|
||||||
|
<t-form ref="form" :data="formData" :rules="rules" :label-width="100" @submit="onSubmit">
|
||||||
|
<t-form-item label="产品名称" name="name">
|
||||||
|
<t-input v-model="formData.name" :style="{ width: '480px' }" placeholder="请输入产品名称" />
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="产品状态" name="status">
|
||||||
|
<t-radio-group v-model="formData.status">
|
||||||
|
<t-radio value="0"> 已停用 </t-radio>
|
||||||
|
<t-radio value="1"> 已启用 </t-radio>
|
||||||
|
</t-radio-group>
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="产品描述" name="description">
|
||||||
|
<t-input v-model="formData.description" :style="{ width: '480px' }" placeholder="请输入产品描述" />
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="产品类型" name="type">
|
||||||
|
<t-select v-model="formData.type" clearable :style="{ width: '480px' }">
|
||||||
|
<t-option v-for="(item, index) in SELECT_OPTIONS" :key="index" :value="item.value" :label="item.label">
|
||||||
|
{{ item.label }}
|
||||||
|
</t-option>
|
||||||
|
</t-select>
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="备注" name="mark">
|
||||||
|
<t-textarea v-model="textareaValue" :style="{ width: '480px' }" placeholder="请输入内容" name="description" />
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item style="float: right">
|
||||||
|
<t-button variant="outline" @click="onClickCloseBtn"> 取消 </t-button>
|
||||||
|
<t-button theme="primary" type="submit"> 确定 </t-button>
|
||||||
|
</t-form-item>
|
||||||
|
</t-form>
|
||||||
|
</template>
|
||||||
|
</t-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref, watch } from 'vue';
|
||||||
|
import { MessagePlugin } from 'tdesign-vue-next';
|
||||||
|
|
||||||
|
const INITIAL_DATA = {
|
||||||
|
name: '',
|
||||||
|
status: '',
|
||||||
|
description: '',
|
||||||
|
type: '',
|
||||||
|
mark: '',
|
||||||
|
amount: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SELECT_OPTIONS = [
|
||||||
|
{ label: '网关', value: '1' },
|
||||||
|
{ label: '人工智能', value: '2' },
|
||||||
|
{ label: 'CVM', value: '3' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
data: Object,
|
||||||
|
},
|
||||||
|
setup(props, ctx) {
|
||||||
|
const formVisible = ref(false);
|
||||||
|
const formData = ref(props.data);
|
||||||
|
const textareaValue = ref('');
|
||||||
|
|
||||||
|
const onSubmit = ({ result, firstError }) => {
|
||||||
|
if (!firstError) {
|
||||||
|
MessagePlugin.success('提交成功');
|
||||||
|
formVisible.value = false;
|
||||||
|
} else {
|
||||||
|
console.log('Errors: ', result);
|
||||||
|
MessagePlugin.warning(firstError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickCloseBtn = () => {
|
||||||
|
formVisible.value = false;
|
||||||
|
formData.value = { ...INITIAL_DATA };
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => formVisible.value,
|
||||||
|
(val) => {
|
||||||
|
const { emit } = ctx;
|
||||||
|
emit('update:visible', val);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
(val) => {
|
||||||
|
formVisible.value = val;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(val) => {
|
||||||
|
formData.value = val;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
SELECT_OPTIONS,
|
||||||
|
formVisible,
|
||||||
|
formData,
|
||||||
|
textareaValue,
|
||||||
|
onSubmit,
|
||||||
|
onClickCloseBtn,
|
||||||
|
rules: {
|
||||||
|
name: [{ required: true, message: '请输入产品名称', type: 'error' }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
206
src/pages/list/card/index.vue
Normal file
206
src/pages/list/card/index.vue
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
<template>
|
||||||
|
<div class="list-card-operation">
|
||||||
|
<t-button @click="formDialogVisible = true"> 新建产品 </t-button>
|
||||||
|
<div :class="PREFIX + '-search-input'">
|
||||||
|
<t-input v-model="searchValue" placeholder="请输入你需要搜索的内容" clearable>
|
||||||
|
<template #suffix-icon>
|
||||||
|
<t-icon-search v-if="searchValue === ''" size="20px" />
|
||||||
|
</template>
|
||||||
|
</t-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<dialog-form v-model:visible="formDialogVisible" :data="formData" />
|
||||||
|
|
||||||
|
<template v-if="pagination.total > 0 && !dataLoading">
|
||||||
|
<div class="list-card-items">
|
||||||
|
<t-row :gutter="[16, 12]">
|
||||||
|
<t-col
|
||||||
|
v-for="product in productList.slice(
|
||||||
|
pagination.pageSize * (pagination.current - 1),
|
||||||
|
pagination.pageSize * pagination.current,
|
||||||
|
)"
|
||||||
|
:key="product.index"
|
||||||
|
:lg="4"
|
||||||
|
:xs="6"
|
||||||
|
:xl="3"
|
||||||
|
>
|
||||||
|
<card
|
||||||
|
class="list-card-item"
|
||||||
|
:product="product"
|
||||||
|
@delete-item="handleDeleteItem"
|
||||||
|
@manage-product="handleManageProduct"
|
||||||
|
/>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
</div>
|
||||||
|
<div class="list-card-pagination">
|
||||||
|
<t-pagination
|
||||||
|
v-model="pagination.current"
|
||||||
|
v-model:page-size="pagination.pageSize"
|
||||||
|
:total="pagination.total"
|
||||||
|
:page-size-options="[12, 24, 36]"
|
||||||
|
@page-size-change="onPageSizeChange"
|
||||||
|
@current-change="onCurrentChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-else-if="dataLoading" class="list-card-loading">
|
||||||
|
<t-loading size="large" text="加载数据中..." />
|
||||||
|
</div>
|
||||||
|
<t-dialog
|
||||||
|
v-model:visible="confirmVisible"
|
||||||
|
header="是否确认删除产品"
|
||||||
|
:body="confirmBody"
|
||||||
|
:on-cancel="onCancel"
|
||||||
|
@confirm="onConfirmDelete"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref, computed, onMounted } from 'vue';
|
||||||
|
import TIconSearch from 'tdesign-vue-next/lib/icon/search';
|
||||||
|
import { MessagePlugin } from 'tdesign-vue-next';
|
||||||
|
import { PREFIX } from '@/config/global';
|
||||||
|
import Card from './components/Card.vue';
|
||||||
|
import DialogForm from './components/DialogForm.vue';
|
||||||
|
import request from '@/utils/request';
|
||||||
|
import { ResDataType } from '@/interface';
|
||||||
|
|
||||||
|
const INITIAL_DATA = {
|
||||||
|
name: '',
|
||||||
|
status: '',
|
||||||
|
description: '',
|
||||||
|
type: '',
|
||||||
|
mark: '',
|
||||||
|
amount: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ListBase',
|
||||||
|
components: {
|
||||||
|
TIconSearch,
|
||||||
|
Card,
|
||||||
|
DialogForm,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const pagination = ref({ current: 1, pageSize: 12, total: 0 });
|
||||||
|
const deleteProduct = ref(undefined);
|
||||||
|
|
||||||
|
const productList = ref([]);
|
||||||
|
const dataLoading = ref(true);
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
try {
|
||||||
|
const res: ResDataType = await request.get('/api/get-card-list');
|
||||||
|
if (res.code === 0) {
|
||||||
|
const { list = [] } = res.data;
|
||||||
|
productList.value = list;
|
||||||
|
pagination.value = {
|
||||||
|
...pagination.value,
|
||||||
|
total: list.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
} finally {
|
||||||
|
dataLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmBody = computed(() =>
|
||||||
|
deleteProduct.value ? `产品名称:${deleteProduct.value.name}, 产品描述: ${deleteProduct.value?.description}` : '',
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData();
|
||||||
|
});
|
||||||
|
|
||||||
|
const formDialogVisible = ref(false);
|
||||||
|
const searchValue = ref('');
|
||||||
|
const confirmVisible = ref(false);
|
||||||
|
const formData = ref({ ...INITIAL_DATA });
|
||||||
|
|
||||||
|
return {
|
||||||
|
PREFIX,
|
||||||
|
pagination,
|
||||||
|
productList,
|
||||||
|
dataLoading,
|
||||||
|
formDialogVisible,
|
||||||
|
confirmBody,
|
||||||
|
searchValue,
|
||||||
|
confirmVisible,
|
||||||
|
formData,
|
||||||
|
onPageSizeChange(size: number) {
|
||||||
|
pagination.value.pageSize = size;
|
||||||
|
pagination.value.current = 1;
|
||||||
|
},
|
||||||
|
onCurrentChange(current: number) {
|
||||||
|
pagination.value.current = current;
|
||||||
|
},
|
||||||
|
handleDeleteItem(product) {
|
||||||
|
confirmVisible.value = true;
|
||||||
|
deleteProduct.value = product;
|
||||||
|
},
|
||||||
|
onConfirmDelete() {
|
||||||
|
const { index } = deleteProduct.value;
|
||||||
|
productList.value.splice(index - 1, 1);
|
||||||
|
confirmVisible.value = false;
|
||||||
|
MessagePlugin.success('删除成功');
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
deleteProduct.value = undefined;
|
||||||
|
formData.value = { ...INITIAL_DATA };
|
||||||
|
},
|
||||||
|
handleManageProduct(product) {
|
||||||
|
formDialogVisible.value = true;
|
||||||
|
formData.value = { ...product, status: product?.isSetup ? '1' : '0' };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import '@/style/variables.less';
|
||||||
|
|
||||||
|
.list-card {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&-operation {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-items {
|
||||||
|
margin-top: 14px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-pagination {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-loading {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{prefix} {
|
||||||
|
&-panel {
|
||||||
|
background-color: @bg-color-container;
|
||||||
|
padding: @spacer-3;
|
||||||
|
border-radius: @border-radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-search-input {
|
||||||
|
width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-operater-row {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
320
src/pages/list/components/Table.vue
Normal file
320
src/pages/list/components/Table.vue
Normal file
|
@ -0,0 +1,320 @@
|
||||||
|
<template>
|
||||||
|
<div :class="`${PREFIX}-list-table`">
|
||||||
|
<t-form
|
||||||
|
ref="form"
|
||||||
|
:data="formData"
|
||||||
|
scroll-to-first-error="smooth"
|
||||||
|
layout="inline"
|
||||||
|
:class="`${PREFIX}-list-table-form`"
|
||||||
|
:style="{ height: isExpand ? '' : '32px' }"
|
||||||
|
:label-width="39"
|
||||||
|
colon
|
||||||
|
@reset="onReset"
|
||||||
|
@submit="onSubmit"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<t-form-item label="合同名称" name="name">
|
||||||
|
<t-input
|
||||||
|
v-model="formData.name"
|
||||||
|
:class="`${PREFIX}-form-item-content`"
|
||||||
|
type="search"
|
||||||
|
placeholder="请输入合同名称"
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="合同状态" name="status">
|
||||||
|
<t-select
|
||||||
|
v-model="formData.status"
|
||||||
|
:class="`${PREFIX}-form-item-content`"
|
||||||
|
:options="CONTRACT_STATUS_OPTIONS"
|
||||||
|
placeholder="请选择合同状态"
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="合同编号" name="no">
|
||||||
|
<t-input v-model="formData.no" :class="`${PREFIX}-form-item-content`" placeholder="请输入合同编号" />
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="合同类型" name="type">
|
||||||
|
<t-select
|
||||||
|
v-model="formData.type"
|
||||||
|
:class="`${PREFIX}-form-item-content`"
|
||||||
|
:options="CONTRACT_TYPE_OPTIONS"
|
||||||
|
placeholder="请选择合同类型"
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
|
</div>
|
||||||
|
<div :class="`${PREFIX}-list-table-form-operation`">
|
||||||
|
<t-form-item :style="operationStyle">
|
||||||
|
<t-button theme="primary" type="submit"> 查询 </t-button>
|
||||||
|
<t-button type="reset" variant="base" theme="default"> 重置 </t-button>
|
||||||
|
<div class="expand" @click="toggleExpand">
|
||||||
|
{{ isExpand ? '收起' : '展开'
|
||||||
|
}}<t-icon-chevron-down size="20" :style="{ transform: `rotate(${isExpand ? '180deg' : '0'}` }" />
|
||||||
|
</div>
|
||||||
|
</t-form-item>
|
||||||
|
</div>
|
||||||
|
</t-form>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<t-table
|
||||||
|
:data="data"
|
||||||
|
:columns="COLUMNS"
|
||||||
|
:row-key="rowKey"
|
||||||
|
:vertical-align="verticalAlign"
|
||||||
|
:hover="hover"
|
||||||
|
:pagination="pagination"
|
||||||
|
:loading="dataLoading"
|
||||||
|
@page-change="rehandlePageChange"
|
||||||
|
@change="rehandleChange"
|
||||||
|
>
|
||||||
|
<template #status="{ row }">
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.FAIL" theme="danger" variant="light"> 审核失败 </t-tag>
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.AUDIT_PENDING" theme="warning" variant="light"> 待审核 </t-tag>
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.EXEC_PENDING" theme="warning" variant="light"> 待履行 </t-tag>
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.EXECUTING" theme="success" variant="light"> 履行中 </t-tag>
|
||||||
|
<t-tag v-if="row.status === CONTRACT_STATUS.FINISH" theme="success" variant="light"> 已完成 </t-tag>
|
||||||
|
</template>
|
||||||
|
<template #contractType="{ row }">
|
||||||
|
<p v-if="row.contractType === CONTRACT_TYPES.MAIN">审核失败</p>
|
||||||
|
<p v-if="row.contractType === CONTRACT_TYPES.SUB">待审核</p>
|
||||||
|
<p v-if="row.contractType === CONTRACT_TYPES.SUPPLEMENT">待履行</p>
|
||||||
|
</template>
|
||||||
|
<template #paymentType="{ row }">
|
||||||
|
<p v-if="row.paymentType === CONTRACT_PAYMENT_TYPES.PAYMENT" class="payment-col">
|
||||||
|
付款<trend class="dashboard-item-trend" type="up" />
|
||||||
|
</p>
|
||||||
|
<p v-if="row.paymentType === CONTRACT_PAYMENT_TYPES.RECIPT" class="payment-col">
|
||||||
|
收款<trend class="dashboard-item-trend" type="down" />
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template #op="slotProps">
|
||||||
|
<a :class="PREFIX + '-link'" @click="rehandleClickOp(slotProps)">管理</a>
|
||||||
|
<a :class="PREFIX + '-link'" @click="handleClickDelete(slotProps)">删除</a>
|
||||||
|
</template>
|
||||||
|
</t-table>
|
||||||
|
<t-dialog
|
||||||
|
v-model:visible="confirmVisible"
|
||||||
|
header="是否确认删除该产品"
|
||||||
|
:body="confirmBody"
|
||||||
|
:on-cancel="onCancel"
|
||||||
|
@confirm="onConfirmDelete"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref, computed, onMounted } from 'vue';
|
||||||
|
import TIconChevronDown from 'tdesign-vue-next/lib/icon/chevron-down';
|
||||||
|
import { MessagePlugin } from 'tdesign-vue-next';
|
||||||
|
import { PREFIX } from '@/config/global';
|
||||||
|
import Trend from '@/components/trend/index.vue';
|
||||||
|
import { COLUMNS } from './constants';
|
||||||
|
import request from '@/utils/request';
|
||||||
|
import { ResDataType } from '@/interface';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CONTRACT_STATUS,
|
||||||
|
CONTRACT_STATUS_OPTIONS,
|
||||||
|
CONTRACT_TYPES,
|
||||||
|
CONTRACT_TYPE_OPTIONS,
|
||||||
|
CONTRACT_PAYMENT_TYPES,
|
||||||
|
} from '@/constants';
|
||||||
|
|
||||||
|
const searchForm = {
|
||||||
|
name: '',
|
||||||
|
no: undefined,
|
||||||
|
status: undefined,
|
||||||
|
type: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ListTable',
|
||||||
|
components: {
|
||||||
|
Trend,
|
||||||
|
TIconChevronDown,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const formData = ref({ ...searchForm });
|
||||||
|
const tableConfig = {
|
||||||
|
rowKey: 'index',
|
||||||
|
verticalAlign: 'top',
|
||||||
|
hover: true,
|
||||||
|
};
|
||||||
|
const pagination = ref({
|
||||||
|
defaultPageSize: 20,
|
||||||
|
total: 100,
|
||||||
|
defaultCurrent: 1,
|
||||||
|
});
|
||||||
|
const confirmVisible = ref(false);
|
||||||
|
const isExpand = ref(false);
|
||||||
|
const toggleExpand = () => {
|
||||||
|
isExpand.value = !isExpand.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const operationStyle = computed(() => {
|
||||||
|
const basisStyle = {
|
||||||
|
position: 'absolute',
|
||||||
|
};
|
||||||
|
return isExpand.value ? { ...basisStyle, bottom: '24px' } : { ...basisStyle, top: 0 };
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = ref([]);
|
||||||
|
|
||||||
|
const dataLoading = ref(false);
|
||||||
|
const fetchData = async () => {
|
||||||
|
dataLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res: ResDataType = await request.get('/api/get-list');
|
||||||
|
if (res.code === 0) {
|
||||||
|
const { list = [] } = res.data;
|
||||||
|
data.value = list;
|
||||||
|
pagination.value = {
|
||||||
|
...pagination.value,
|
||||||
|
total: list.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
} finally {
|
||||||
|
dataLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteIdx = ref(-1);
|
||||||
|
const confirmBody = computed(() => {
|
||||||
|
if (deleteIdx.value > -1) {
|
||||||
|
const { no, name } = data.value[deleteIdx.value];
|
||||||
|
return `产品编号:${no}, 产品名称: ${name}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
const resetIdx = () => {
|
||||||
|
deleteIdx.value = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConfirmDelete = () => {
|
||||||
|
// 真实业务请发起请求
|
||||||
|
data.value.splice(deleteIdx.value - 1, 1);
|
||||||
|
pagination.value.total = data.value.length;
|
||||||
|
confirmVisible.value = false;
|
||||||
|
MessagePlugin.success('删除成功');
|
||||||
|
resetIdx();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
resetIdx();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
PREFIX,
|
||||||
|
data,
|
||||||
|
COLUMNS,
|
||||||
|
CONTRACT_STATUS,
|
||||||
|
CONTRACT_STATUS_OPTIONS,
|
||||||
|
CONTRACT_TYPES,
|
||||||
|
CONTRACT_TYPE_OPTIONS,
|
||||||
|
CONTRACT_PAYMENT_TYPES,
|
||||||
|
formData,
|
||||||
|
pagination,
|
||||||
|
confirmVisible,
|
||||||
|
operationStyle,
|
||||||
|
confirmBody,
|
||||||
|
...tableConfig,
|
||||||
|
onConfirmDelete,
|
||||||
|
onCancel,
|
||||||
|
dataLoading,
|
||||||
|
handleClickDelete({ row }) {
|
||||||
|
deleteIdx.value = row.index;
|
||||||
|
confirmVisible.value = true;
|
||||||
|
},
|
||||||
|
onReset(val) {
|
||||||
|
console.log(val);
|
||||||
|
},
|
||||||
|
onSubmit(val) {
|
||||||
|
console.log(val);
|
||||||
|
},
|
||||||
|
rehandlePageChange(curr, pageInfo) {
|
||||||
|
console.log('分页变化', curr, pageInfo);
|
||||||
|
},
|
||||||
|
rehandleChange(changeParams, triggerAndData) {
|
||||||
|
console.log('统一Change', changeParams, triggerAndData);
|
||||||
|
},
|
||||||
|
rehandleClickOp({ text, row }) {
|
||||||
|
console.log(text, row);
|
||||||
|
},
|
||||||
|
isExpand,
|
||||||
|
toggleExpand,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import '@/style/index';
|
||||||
|
.@{prefix} {
|
||||||
|
&-list-table {
|
||||||
|
background-color: @bg-color-container;
|
||||||
|
padding: 30px 32px;
|
||||||
|
border-radius: @border-radius;
|
||||||
|
|
||||||
|
&-form {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: 24px;
|
||||||
|
|
||||||
|
> div:first-child {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.t-form__item {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-operation {
|
||||||
|
width: 280px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand {
|
||||||
|
margin-left: 16px;
|
||||||
|
color: @brand-color;
|
||||||
|
line-height: 22px;
|
||||||
|
height: 22px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-operater-row {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-form-item-content {
|
||||||
|
width: 240px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-col {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.trend-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
91
src/pages/list/tree/index.vue
Normal file
91
src/pages/list/tree/index.vue
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
<template>
|
||||||
|
<div :class="`${PREFIX}-tree-panel`">
|
||||||
|
<div class="list-tree-wrapper">
|
||||||
|
<div class="list-tree-operator">
|
||||||
|
<t-input v-model="filterText" placeholder="请输入关键词" @input="onInput">
|
||||||
|
<template #suffix-icon>
|
||||||
|
<t-icon-search size="20px" />
|
||||||
|
</template>
|
||||||
|
</t-input>
|
||||||
|
<t-tree :data="TREE_DATA" hover expand-on-click-node :default-expanded="expanded" :filter="filterByText" />
|
||||||
|
</div>
|
||||||
|
<div class="list-tree-content">
|
||||||
|
<list-common-table />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, ref } from 'vue';
|
||||||
|
import TIconSearch from 'tdesign-vue-next/lib/icon/search';
|
||||||
|
import { PREFIX } from '@/config/global';
|
||||||
|
|
||||||
|
import { TREE_DATA } from './constants';
|
||||||
|
import ListCommonTable from '../components/Table.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ListTree',
|
||||||
|
components: {
|
||||||
|
TIconSearch,
|
||||||
|
ListCommonTable,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const filterByText = ref();
|
||||||
|
const filterText = ref();
|
||||||
|
return {
|
||||||
|
PREFIX,
|
||||||
|
TREE_DATA,
|
||||||
|
expanded: ['0', '0-0', '0-1', '0-2', '0-3', '0-4'],
|
||||||
|
filterText,
|
||||||
|
filterByText,
|
||||||
|
onInput() {
|
||||||
|
filterByText.value = (node) => {
|
||||||
|
const rs = node.label.indexOf(filterText.value) >= 0;
|
||||||
|
return rs;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import '@/style/variables.less';
|
||||||
|
.@{prefix} {
|
||||||
|
&-tree-panel {
|
||||||
|
background-color: @bg-color-container;
|
||||||
|
border-radius: @border-radius;
|
||||||
|
|
||||||
|
.t-tree {
|
||||||
|
margin-top: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-search-input {
|
||||||
|
width: 360px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-operater-row {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-tree-wrapper {
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-tree-operator {
|
||||||
|
width: 200px;
|
||||||
|
float: left;
|
||||||
|
padding: 30px 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-tree-content {
|
||||||
|
border-left: 1px solid @border-level-1-color;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selet-base {
|
||||||
|
width: 88px;
|
||||||
|
}
|
||||||
|
</style>
|
164
src/pages/login/components/Login.vue
Normal file
164
src/pages/login/components/Login.vue
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
<template>
|
||||||
|
<!-- 密码登陆 -->
|
||||||
|
<div v-if="type == 'pwd'" class="item-container login-pwd">
|
||||||
|
<div class="input-container">
|
||||||
|
<t-input v-model="userInfo.EngName" style="width: 400px" size="large" placeholder="请输入您的邮箱/手机号">
|
||||||
|
<template #prefix-icon>
|
||||||
|
<t-icon name="user" />
|
||||||
|
</template>
|
||||||
|
</t-input>
|
||||||
|
<t-popup placement="right" trigger="focus" show-arrow>
|
||||||
|
<t-input
|
||||||
|
v-model="psw"
|
||||||
|
style="width: 400px"
|
||||||
|
size="large"
|
||||||
|
:type="showPsw ? 'text' : 'password'"
|
||||||
|
clearablec
|
||||||
|
placeholder="请输入密码"
|
||||||
|
@keyup="checkPsw"
|
||||||
|
>
|
||||||
|
<template #prefix-icon>
|
||||||
|
<t-icon name="lock-on" />
|
||||||
|
</template>
|
||||||
|
<template #suffix-icon>
|
||||||
|
<t-icon :name="showPsw ? 'browse' : 'browse-off'" @click="showPsw = !showPsw" />
|
||||||
|
</template>
|
||||||
|
</t-input>
|
||||||
|
<template #content>
|
||||||
|
<div>
|
||||||
|
<div :class="['rex-check', { 'format-correct': check1 }]">
|
||||||
|
<t-icon name="check-circle-filled" size="large" />
|
||||||
|
<span>1-20个英文字符</span>
|
||||||
|
</div>
|
||||||
|
<div :class="['rex-check', { 'format-correct': check2 }]">
|
||||||
|
<t-icon name="check-circle-filled" size="large" />
|
||||||
|
<span>需包含下划线</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</t-popup>
|
||||||
|
<div class="check-container">
|
||||||
|
<t-checkbox>记住账号</t-checkbox>
|
||||||
|
<span class="tip">忘记账号?</span>
|
||||||
|
</div>
|
||||||
|
<t-button class="button-container" style="width: 400px" size="large" @click="handleClickToLogIn"> 登录 </t-button>
|
||||||
|
</div>
|
||||||
|
<div class="bottom-container">
|
||||||
|
<span class="tip" @click="switchType('qrcode')">使用微信扫码登录</span>
|
||||||
|
<i>|</i>
|
||||||
|
<span class="tip" @click="switchType('phone')">使用短信登录</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 扫码登陆 -->
|
||||||
|
<div v-else-if="type == 'qrcode'" class="item-container login-qrcode">
|
||||||
|
<div class="input-container">
|
||||||
|
<div class="tip-container">
|
||||||
|
<span class="tip1">请使用微信扫一扫登录</span>
|
||||||
|
<span class="tip2 refresh">刷新 <t-icon name="refresh" color="#0052D9" /> </span>
|
||||||
|
</div>
|
||||||
|
<qrcode-vue value="" :size="192" level="H" />
|
||||||
|
</div>
|
||||||
|
<div class="bottom-container">
|
||||||
|
<span class="tip" @click="switchType('pwd')">使用账号密码登录</span>
|
||||||
|
<i>|</i>
|
||||||
|
<span class="tip" @click="switchType('phone')">使用短信登录</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 手机号登陆 -->
|
||||||
|
<div v-else class="item-container login-phone">
|
||||||
|
<div class="input-container">
|
||||||
|
<t-input v-model="userInfo.EngName" style="width: 400px" size="large" placeholder="请输入您的手机号">
|
||||||
|
<template #prefix-icon>
|
||||||
|
<t-icon name="user" />
|
||||||
|
</template>
|
||||||
|
</t-input>
|
||||||
|
<div class="verification-code">
|
||||||
|
<t-input style="width: 282px" size="large" placeholder="请输入验证码" />
|
||||||
|
<t-button variant="outline" :disabled="countDown > 0" @click="handleCounter">
|
||||||
|
{{ countDown == 0 ? '发送验证码' : `${countDown}秒后可重发` }}
|
||||||
|
</t-button>
|
||||||
|
</div>
|
||||||
|
<t-button class="button-container" style="width: 400px" size="large" @click="handleClickToLogIn"> 登录 </t-button>
|
||||||
|
</div>
|
||||||
|
<div class="bottom-container">
|
||||||
|
<span class="tip" @click="switchType('pwd')">使用账号密码登录</span>
|
||||||
|
<i>|</i>
|
||||||
|
<span class="tip" @click="switchType('qrcode')">使用微信扫码登录</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, 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 '@/utils/hooks';
|
||||||
|
|
||||||
|
const INITIAL_DATA = {
|
||||||
|
EngName: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { QrcodeVue },
|
||||||
|
setup() {
|
||||||
|
const type = ref('pwd');
|
||||||
|
const psw = ref('');
|
||||||
|
const check1 = ref(false);
|
||||||
|
const check2 = ref(false);
|
||||||
|
|
||||||
|
const userInfo = ref({ ...INITIAL_DATA });
|
||||||
|
const showPsw = ref(false);
|
||||||
|
|
||||||
|
const [countDown, handleCounter] = useCounter();
|
||||||
|
|
||||||
|
const checkPsw = () => {
|
||||||
|
const regExp = /^[a-z0-9_]{1,20}$/;
|
||||||
|
if (regExp.test(psw.value)) {
|
||||||
|
check1.value = true;
|
||||||
|
} else {
|
||||||
|
check1.value = false;
|
||||||
|
}
|
||||||
|
if (psw.value.indexOf('_') !== -1) {
|
||||||
|
check2.value = true;
|
||||||
|
} else {
|
||||||
|
check2.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const switchType = (val: string) => {
|
||||||
|
type.value = val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
|
const handleClickToLogIn = () => {
|
||||||
|
store.commit('user/SET_USER_INFO', userInfo.value);
|
||||||
|
|
||||||
|
MessagePlugin.success('登录成功');
|
||||||
|
|
||||||
|
router.push({
|
||||||
|
path: '/',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
showPsw,
|
||||||
|
psw,
|
||||||
|
userInfo,
|
||||||
|
checkPsw,
|
||||||
|
check1,
|
||||||
|
check2,
|
||||||
|
type,
|
||||||
|
switchType,
|
||||||
|
countDown,
|
||||||
|
handleCounter,
|
||||||
|
handleClickToLogIn,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
74
src/pages/user/constants.ts
Normal file
74
src/pages/user/constants.ts
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
export const USER_INFO_LIST = [
|
||||||
|
{
|
||||||
|
title: '手机',
|
||||||
|
content: '+86 13923734567',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '座机',
|
||||||
|
content: '734567',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '办公室邮箱',
|
||||||
|
content: 'Account@qq.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '座位',
|
||||||
|
content: 'T32F 012',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '管理主体',
|
||||||
|
content: '腾讯集团',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '直属上级',
|
||||||
|
content: 'Account@qq.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '直属上级',
|
||||||
|
content: '高级 UI 设计师',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '入职时间',
|
||||||
|
content: '2021-07-01',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '所属团队',
|
||||||
|
content: '腾讯/腾讯公司/某事业群/某产品部/某运营中心/商户服务组',
|
||||||
|
span: 6,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const TEAM_MEMBERS = [
|
||||||
|
{
|
||||||
|
avatar: 'https://tdesign.gtimg.com/pro-template/personal/avatar5.png',
|
||||||
|
title: 'Lovellzhang 张庆霖',
|
||||||
|
description: '直客销售 港澳拓展组员工',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'https://tdesign.gtimg.com/pro-template/personal/avatar1.png',
|
||||||
|
title: 'Jiajingwang 王佳静',
|
||||||
|
description: '前端开发 前台研发组员工',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'https://tdesign.gtimg.com/pro-template/personal/avatar5.png',
|
||||||
|
title: 'cruisezhang 张超',
|
||||||
|
description: '技术产品 产品组员工',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'https://tdesign.gtimg.com/pro-template/personal/avatar3.png',
|
||||||
|
title: 'Lovellzhang 张庆霖',
|
||||||
|
description: '产品运营 港澳拓展组员工',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const PRODUCT_LIST = [
|
||||||
|
{
|
||||||
|
logo: 'https://tdesign.gtimg.com/pro-template/tdesign-icon1.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
logo: 'https://tdesign.gtimg.com/pro-template/tdesign-icon2.png',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
logo: 'https://tdesign.gtimg.com/pro-template/tdesign-icon3.png',
|
||||||
|
},
|
||||||
|
];
|
154
src/pages/user/index.vue
Normal file
154
src/pages/user/index.vue
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="user-panel">
|
||||||
|
<t-row>
|
||||||
|
<t-col class="user-left-panel" :flex="3">
|
||||||
|
<div class="user-top">
|
||||||
|
<div class="user-left-greeting">
|
||||||
|
Hi,Image
|
||||||
|
<span class="regular"> 下午好,今天是你加入鹅厂的第 100 天~</span>
|
||||||
|
<img src="../../assets/tencent-logo.png" class="user-left-logo" />
|
||||||
|
</div>
|
||||||
|
<div class="user-right-info">
|
||||||
|
<div class="head-bar">
|
||||||
|
<div class="title">个人信息</div>
|
||||||
|
<t-icon name="edit" size="18" />
|
||||||
|
</div>
|
||||||
|
<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">
|
||||||
|
<div class="contract-title">
|
||||||
|
{{ item.title }}
|
||||||
|
</div>
|
||||||
|
<div class="contract-detail">
|
||||||
|
{{ item.content }}
|
||||||
|
</div>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="user-bottom">
|
||||||
|
<div class="t-demo-tabs">
|
||||||
|
<t-tabs default-value="second">
|
||||||
|
<t-tab-panel value="first" label="内容列表">
|
||||||
|
<p style="padding: 25px">内容列表</p>
|
||||||
|
</t-tab-panel>
|
||||||
|
<t-tab-panel value="second" label="内容列表">
|
||||||
|
<div class="user-bottom-container">
|
||||||
|
<div class="head-bar">
|
||||||
|
<div class="title">主页访问数据<span class="unit">(次)</span></div>
|
||||||
|
<t-date-picker
|
||||||
|
class="time-picker"
|
||||||
|
:default-value="LAST_7_DAYS"
|
||||||
|
theme="primary"
|
||||||
|
mode="date"
|
||||||
|
range
|
||||||
|
@change="onLineChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div id="lineContainer" style="width: 100%; height: 330px" />
|
||||||
|
</div>
|
||||||
|
</t-tab-panel>
|
||||||
|
<t-tab-panel value="third" label="内容列表">
|
||||||
|
<p style="padding: 25px">内容列表</p>
|
||||||
|
</t-tab-panel>
|
||||||
|
</t-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t-col>
|
||||||
|
|
||||||
|
<t-col class="user-right-panel" :flex="1">
|
||||||
|
<div class="user-top">
|
||||||
|
<div class="account">
|
||||||
|
<img class="img" src="https://tdesign.gtimg.com/pro-template/personal/avatar4.png" />
|
||||||
|
<div class="name">My Account</div>
|
||||||
|
<div class="position">XXG 港澳业务拓展组员工 直客销售</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="user-middle">
|
||||||
|
<div class="head-bar">
|
||||||
|
<div class="title">团队成员</div>
|
||||||
|
<t-icon name="edit" size="18" />
|
||||||
|
</div>
|
||||||
|
<t-list :split="false">
|
||||||
|
<t-list-item v-for="(item, index) in TEAM_MEMBERS" :key="index">
|
||||||
|
<t-list-item-meta :image="item.avatar" :title="item.title" :description="item.description" />
|
||||||
|
</t-list-item>
|
||||||
|
</t-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="user-bottom">
|
||||||
|
<div class="head-bar">
|
||||||
|
<div class="title">服务产品</div>
|
||||||
|
<t-icon name="edit" size="18" />
|
||||||
|
</div>
|
||||||
|
<t-row class="content" justify="space-between">
|
||||||
|
<t-col v-for="(item, index) in PRODUCT_LIST" :key="index" class="contract" :span="4">
|
||||||
|
<img :src="item.logo" class="user-right-logo" />
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
</div>
|
||||||
|
</t-col>
|
||||||
|
</t-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, onMounted, watch } from 'vue';
|
||||||
|
import { DateValue } from 'tdesign-vue-next';
|
||||||
|
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 { LAST_7_DAYS } from '@/utils/date';
|
||||||
|
import { useChart } from '@/utils/hooks';
|
||||||
|
import { USER_INFO_LIST, TEAM_MEMBERS, PRODUCT_LIST } from './constants';
|
||||||
|
import { changeChartsTheme, getFolderlineDataSet } from '@/pages/dashboard/base/index';
|
||||||
|
|
||||||
|
echarts.use([GridComponent, TooltipComponent, LineChart, CanvasRenderer, LegendComponent]);
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
setup() {
|
||||||
|
const lineChart = useChart('lineContainer');
|
||||||
|
|
||||||
|
const onLineChange = (value: DateValue) => {
|
||||||
|
lineChart.value.setOption(getFolderlineDataSet(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
lineChart.value.setOption({
|
||||||
|
grid: {
|
||||||
|
x: 30, // 默认是80px
|
||||||
|
y: 30, // 默认是60px
|
||||||
|
x2: 10, // 默认80px
|
||||||
|
y2: 30, // 默认60px
|
||||||
|
},
|
||||||
|
...getFolderlineDataSet(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
watch(
|
||||||
|
() => store.state.setting.brandTheme,
|
||||||
|
(val) => {
|
||||||
|
changeChartsTheme([lineChart.value], val);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
LAST_7_DAYS,
|
||||||
|
USER_INFO_LIST,
|
||||||
|
TEAM_MEMBERS,
|
||||||
|
PRODUCT_LIST,
|
||||||
|
onLineChange,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url('./index.less');
|
||||||
|
</style>
|
250
src/style/variables.less
Normal file
250
src/style/variables.less
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
/** 公共前缀 */
|
||||||
|
@prefix: tdesign-pro;
|
||||||
|
|
||||||
|
// 颜色色板
|
||||||
|
|
||||||
|
@brand-color-1: var(--td-brand-color-1);
|
||||||
|
@brand-color-2: var(--td-brand-color-2);
|
||||||
|
@brand-color-3: var(--td-brand-color-3);
|
||||||
|
@brand-color-4: var(--td-brand-color-4);
|
||||||
|
@brand-color-5: var(--td-brand-color-5);
|
||||||
|
@brand-color-6: var(--td-brand-color-6);
|
||||||
|
@brand-color-7: var(--td-brand-color-7);
|
||||||
|
@brand-color-8: var(--td-brand-color-8);
|
||||||
|
@brand-color-9: var(--td-brand-color-9);
|
||||||
|
@brand-color-10: var(--td-brand-color-10);
|
||||||
|
|
||||||
|
@warning-color-1: var(--td-warning-color-1);
|
||||||
|
@warning-color-2: var(--td-warning-color-2);
|
||||||
|
@warning-color-3: var(--td-warning-color-3);
|
||||||
|
@warning-color-4: var(--td-warning-color-4);
|
||||||
|
@warning-color-5: var(--td-warning-color-5);
|
||||||
|
@warning-color-6: var(--td-warning-color-6);
|
||||||
|
@warning-color-7: var(--td-warning-color-7);
|
||||||
|
@warning-color-8: var(--td-warning-color-8);
|
||||||
|
@warning-color-9: var(--td-warning-color-9);
|
||||||
|
@warning-color-10: var(--td-warning-color-10);
|
||||||
|
|
||||||
|
@error-color-1: var(--td-error-color-1);
|
||||||
|
@error-color-2: var(--td-error-color-2);
|
||||||
|
@error-color-3: var(--td-error-color-3);
|
||||||
|
@error-color-4: var(--td-error-color-4);
|
||||||
|
@error-color-5: var(--td-error-color-5);
|
||||||
|
@error-color-6: var(--td-error-color-6);
|
||||||
|
@error-color-7: var(--td-error-color-7);
|
||||||
|
@error-color-8: var(--td-error-color-8);
|
||||||
|
@error-color-9: var(--td-error-color-9);
|
||||||
|
@error-color-10: var(--td-error-color-10);
|
||||||
|
|
||||||
|
@success-color-1: var(--td-success-color-1);
|
||||||
|
@success-color-2: var(--td-success-color-2);
|
||||||
|
@success-color-3: var(--td-success-color-3);
|
||||||
|
@success-color-4: var(--td-success-color-4);
|
||||||
|
@success-color-5: var(--td-success-color-5);
|
||||||
|
@success-color-6: var(--td-success-color-6);
|
||||||
|
@success-color-7: var(--td-success-color-7);
|
||||||
|
@success-color-8: var(--td-success-color-8);
|
||||||
|
@success-color-9: var(--td-success-color-9);
|
||||||
|
@success-color-10: var(--td-success-color-10);
|
||||||
|
|
||||||
|
@gray-color-1: var(--td-gray-color-1);
|
||||||
|
@gray-color-2: var(--td-gray-color-2);
|
||||||
|
@gray-color-3: var(--td-gray-color-3);
|
||||||
|
@gray-color-4: var(--td-gray-color-4);
|
||||||
|
@gray-color-5: var(--td-gray-color-5);
|
||||||
|
@gray-color-6: var(--td-gray-color-6);
|
||||||
|
@gray-color-7: var(--td-gray-color-7);
|
||||||
|
@gray-color-8: var(--td-gray-color-8);
|
||||||
|
@gray-color-9: var(--td-gray-color-9);
|
||||||
|
@gray-color-10: var(--td-gray-color-10);
|
||||||
|
@gray-color-11: var(--td-gray-color-11);
|
||||||
|
@gray-color-12: var(--td-gray-color-12);
|
||||||
|
@gray-color-13: var(--td-gray-color-13);
|
||||||
|
@gray-color-14: var(--td-gray-color-14);
|
||||||
|
|
||||||
|
// 文字 & 图标 颜色
|
||||||
|
@font-white-1: var(--td-font-white-1);
|
||||||
|
@font-white-2: var(--td-font-white-2);
|
||||||
|
@font-white-3: var(--td-font-white-3);
|
||||||
|
@font-white-4: var(--td-font-white-4);
|
||||||
|
|
||||||
|
@font-gray-1: var(--td-font-gray-1);
|
||||||
|
@font-gray-2: var(--td-font-gray-2);
|
||||||
|
@font-gray-3: var(--td-font-gray-3);
|
||||||
|
@font-gray-4: var(--td-font-gray-4);
|
||||||
|
|
||||||
|
// 基础颜色
|
||||||
|
@brand-color: var(--td-brand-color); // 色彩-品牌-可操作
|
||||||
|
@warning-color: var(--td-warning-color); // 色彩-功能-警告
|
||||||
|
@error-color: var(--td-error-color); // 色彩-功能-失败
|
||||||
|
@success-color: var(--td-success-color); // 色彩-功能-成功
|
||||||
|
|
||||||
|
|
||||||
|
// 基础颜色的扩展 用于 hover / 聚焦 / 禁用 / 点击 等状态
|
||||||
|
@brand-color-hover: var(--td-brand-color-hover); // hover态
|
||||||
|
@brand-color-focus: var(--td-brand-color-focus); // focus态,包括鼠标和键盘
|
||||||
|
@brand-color-active: var(--td-brand-color-active); // 点击态
|
||||||
|
@brand-color-disabled: var(--td-brand-color-disabled); // 禁用态
|
||||||
|
@brand-color-light: var(--td-brand-color-light); // 浅色的选中态
|
||||||
|
|
||||||
|
// 警告色扩展
|
||||||
|
@warning-color-hover: var(--td-warning-color-hover);
|
||||||
|
@warning-color-focus: var(--td-warning-color-focus);
|
||||||
|
@warning-color-active: var(--td-warning-color-active);
|
||||||
|
@warning-color-disabled: var(--td-warning-color-disabled);
|
||||||
|
@warning-color-light: var(--td-warning-color-light);
|
||||||
|
|
||||||
|
// 失败/错误色扩展
|
||||||
|
@error-color-hover: var(--td-error-color-hover);
|
||||||
|
@error-color-focus: var(--td-error-color-focus);
|
||||||
|
@error-color-active: var(--td-error-color-active);
|
||||||
|
@error-color-disabled: var(--td-error-color-disabled);
|
||||||
|
@error-color-light: var(--td-error-color-light);
|
||||||
|
|
||||||
|
// 成功色扩展
|
||||||
|
@success-color-hover: var(--td-success-color-hover);
|
||||||
|
@success-color-focus: var(--td-success-color-focus);
|
||||||
|
@success-color-active: var(--td-success-color-active);
|
||||||
|
@success-color-disabled: var(--td-success-color-disabled);
|
||||||
|
@success-color-light: var(--td-success-color-light);
|
||||||
|
|
||||||
|
// 遮罩
|
||||||
|
@mask-active: var(--td-mask-active); // 遮罩-弹出
|
||||||
|
@mask-disabled: var(--td-mask-disabled); // 遮罩-禁用
|
||||||
|
|
||||||
|
// 背景色
|
||||||
|
@bg-color-page: var(--td-bg-color-page); // 色彩 - page
|
||||||
|
@bg-color-container: var(--td-bg-color-container); // 色彩 - 容器
|
||||||
|
@bg-color-container-hover: var(--td-bg-color-container-hover); // 色彩 - 容器 - hover
|
||||||
|
@bg-color-container-active: var(--td-bg-color-container-active); // 色彩 - 容器 - active
|
||||||
|
@bg-color-container-select: var(--td-bg-color-container-select); // 色彩 - 容器 - select
|
||||||
|
|
||||||
|
@bg-color-secondarycontainer: var(--td-bg-color-secondarycontainer); // 色彩 - 次级容器
|
||||||
|
@bg-color-secondarycontainer-hover: var(--td-bg-color-secondarycontainer-hover); // 色彩 - 次级容器 - hover
|
||||||
|
@bg-color-secondarycontainer-active: var(--td-bg-color-secondarycontainer-active); // 色彩 - 次级容器 - active
|
||||||
|
|
||||||
|
@bg-color-component: var(--td-bg-color-component); // 色彩 - 组件
|
||||||
|
@bg-color-component-hover: var(--td-bg-color-component-hover); // 色彩 - 组件 - hover
|
||||||
|
@bg-color-component-active: var(--td-bg-color-component-active); // 色彩 - 组件 - active
|
||||||
|
@bg-color-component-disabled: var(--td-bg-color-component-disabled); // 色彩 - 组件 - disabled
|
||||||
|
|
||||||
|
// TODO: 考虑是否在组件内部做判断,不增加额外变量
|
||||||
|
// 特殊组件背景色,目前只用于 button、input 组件多主题场景,浅色主题下固定为白色,深色主题下为 transparent 适配背景颜色
|
||||||
|
@bg-color-specialcomponent: var(--td-bg-color-specialcomponent);
|
||||||
|
|
||||||
|
// 文本颜色
|
||||||
|
@text-color-primary: var(--td-text-color-primary); // 色彩-文字-主要
|
||||||
|
@text-color-secondary: var(--td-text-color-secondary); // 色彩-文字-次要
|
||||||
|
@text-color-placeholder: var(--td-text-color-placeholder); // 色彩-文字-占位符/说明
|
||||||
|
@text-color-disabled: var(--td-text-color-disabled); // 色彩-文字-禁用
|
||||||
|
@text-color-anti: var(--td-text-color-anti); // 色彩-文字-反色
|
||||||
|
@text-color-brand: var(--td-text-color-brand); // 色彩-文字-品牌
|
||||||
|
@text-color-link: var(--td-text-color-link); // 色彩-文字-链接
|
||||||
|
|
||||||
|
// 分割线
|
||||||
|
@border-level-1-color: var(--td-border-level-1-color);
|
||||||
|
@component-stroke: var(--td-component-stroke);
|
||||||
|
// 边框
|
||||||
|
@border-level-2-color: var(--td-border-level-2-color);
|
||||||
|
@component-border: var(--td-component-border);
|
||||||
|
|
||||||
|
// shadow
|
||||||
|
|
||||||
|
// 基础/下层 投影 hover 使用的组件包括:表格 /
|
||||||
|
@shadow-1: var(--td-shadow-1);
|
||||||
|
// 中层投影 下拉 使用的组件包括:下拉菜单 / 气泡确认框 / 选择器 /
|
||||||
|
@shadow-2: var(--td-shadow-2);
|
||||||
|
// 上层投影(警示/弹窗)使用的组件包括:全局提示 / 消息通知
|
||||||
|
@shadow-3: var(--td-shadow-3);
|
||||||
|
|
||||||
|
// 内投影 用于弹窗类组件(气泡确认框 / 全局提示 / 消息通知)的内描边
|
||||||
|
@shadow-inset-top: var(--td-shadow-inset-top);
|
||||||
|
@shadow-inset-right: var(--td-shadow-inset-right);
|
||||||
|
@shadow-inset-bottom: var(--td-shadow-inset-bottom);
|
||||||
|
@shadow-inset-left: var(--td-shadow-inset-left);
|
||||||
|
@shadow-inset: @shadow-inset-top, @shadow-inset-right, @shadow-inset-bottom, @shadow-inset-left;
|
||||||
|
|
||||||
|
// 融合阴影
|
||||||
|
@shadow-2-inset: @shadow-2, @shadow-inset;
|
||||||
|
@shadow-3-inset: @shadow-3, @shadow-inset;
|
||||||
|
|
||||||
|
// Spacer
|
||||||
|
@spacer: 8px;
|
||||||
|
@spacer-s: @spacer * .5; // 间距-4
|
||||||
|
@spacer-l: @spacer * 1.5; // 间距-12
|
||||||
|
@spacer-1: @spacer; // 间距-8
|
||||||
|
@spacer-2: @spacer * 2; // 间距-16
|
||||||
|
@spacer-3: @spacer * 3; // 间距-24
|
||||||
|
@spacer-4: @spacer * 4; // 间距-32
|
||||||
|
@spacer-5: @spacer * 5; // 间距-大-40
|
||||||
|
@spacer-6: @spacer * 6; // 间距-大-48
|
||||||
|
@spacer-7: @spacer * 7; // 间距-大-48
|
||||||
|
@spacer-8: @spacer * 8; // 间距-大-48
|
||||||
|
@spacer-9: @spacer * 9; // 间距-大-48
|
||||||
|
@spacer-10: @spacer * 10; // 间距-大-80
|
||||||
|
|
||||||
|
// Font
|
||||||
|
@font-size: 10px;
|
||||||
|
@font-size-s: @font-size * 1.2; // 字号-五级字号
|
||||||
|
@font-size-base: @font-size * 1.4; // 字号-四级字号
|
||||||
|
@font-size-l: @font-size * 1.6; // 字号-三级字号
|
||||||
|
@font-size-xl: @font-size * 2; // 字号-二级字号
|
||||||
|
@font-size-xxl: @font-size * 3.6; // 字号-一级字号
|
||||||
|
|
||||||
|
// Line Height
|
||||||
|
@text-line-height: 1.5; // 行高-常规
|
||||||
|
@text-line-height-s: 20px; // 行高-对应五级文字
|
||||||
|
@text-line-height-base: 22px; // 行高-对应四级文字
|
||||||
|
@text-line-height-l: 24px; // 行高-对应三级文字
|
||||||
|
@text-line-height-xl: 28px; // 行高-对应二级文字
|
||||||
|
@text-line-height-xxl: 44px; //行高-对应一级文字
|
||||||
|
|
||||||
|
@font-family: PingFang SC, Microsoft YaHei, Arial Regular; // 字体-磅数-常规
|
||||||
|
@font-family-medium: PingFang SC, Microsoft YaHei, Arial Medium; // 字体-磅数-粗体
|
||||||
|
|
||||||
|
// Border Radius
|
||||||
|
@border-radius: 3px; // 圆角-全局
|
||||||
|
@border-radius-50: 50%; // 圆角-全圆角
|
||||||
|
|
||||||
|
// 表单相关
|
||||||
|
@form-height: 30px;
|
||||||
|
@form-text-color: @text-color-primary;
|
||||||
|
@form-bg-color: @bg-color-container;
|
||||||
|
@form-border-color: @border-level-2-color;
|
||||||
|
|
||||||
|
// 图标尺寸
|
||||||
|
@icon-default: 16px;
|
||||||
|
@icon-l: 24px;
|
||||||
|
|
||||||
|
// 滚动条颜色
|
||||||
|
@scrollbar-color: var(--td-scrollbar-color);
|
||||||
|
|
||||||
|
// 响应式断点
|
||||||
|
@screen-sm: 768px;
|
||||||
|
@screen-md: 992px;
|
||||||
|
@screen-lg: 1200px;
|
||||||
|
|
||||||
|
@screen-sm-min: @screen-sm;
|
||||||
|
@screen-md-min: @screen-md;
|
||||||
|
@screen-lg-min: @screen-lg;
|
||||||
|
|
||||||
|
@screen-sm-max: (@screen-md-min - 1px);
|
||||||
|
@screen-md-max: (@screen-lg-min - 1px);
|
||||||
|
|
||||||
|
|
||||||
|
// 动画
|
||||||
|
@anim-time-fn-easing: cubic-bezier(.38, 0, .24, 1);
|
||||||
|
@anim-time-fn-ease-out: cubic-bezier(0, 0, .15, 1);
|
||||||
|
@anim-time-fn-ease-in: cubic-bezier(.82, 0, 1, .9);
|
||||||
|
@anim-duration-base: .2s;
|
||||||
|
@anim-duration-moderate: .24s;
|
||||||
|
@anim-duration-slow: .28s;
|
||||||
|
|
||||||
|
// 统一管理各组件层级关系
|
||||||
|
@z-index-affix: 500;
|
||||||
|
@z-index-drawer: 1500;
|
||||||
|
@z-index-dialog: 2500;
|
||||||
|
@z-index-loading: 3500;
|
||||||
|
@z-index-message: 5000;
|
||||||
|
@z-index-Popup: 5500;
|
||||||
|
@z-index-Notification: 6000;
|
Loading…
Reference in New Issue
Block a user