This commit is contained in:
王思川 2023-08-04 11:03:14 +08:00
parent 2c9ee5074f
commit 2fcd2ff565
40 changed files with 8106 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.idea
node_modules
.DS_Store
dist
dist-ssr
*.local

15
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,15 @@
stages:
- deploy
job:
stage: deploy
script:
- docker stop fecr_sambussiness
- docker rm fecr_sambussiness
- docker build -t fecr_sambussiness .
- docker run -d --restart=always --network=host --name fecr_sambussiness fecr_sambussiness
only:
- master
tags:
- fecr_sambussiness

10
Dockerfile Normal file
View File

@ -0,0 +1,10 @@
FROM node:10
COPY ./ /app
WORKDIR /app
RUN npm config set registry http://registry.npm.taobao.org
RUN npm install && npm run build
FROM nginx
RUN mkdir /app
COPY --from=0 /app/dist /app
COPY nginx.conf /etc/nginx/nginx.conf

16
env.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
declare module "*.vue" {
import type { DefineComponent } from "vue";
const vueComponent: DefineComponent<{}, {}, any>;
export default vueComponent;
}
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'
declare module '*.json'

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

31
nginx.conf Normal file
View File

@ -0,0 +1,31 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 3030;
server_name localhost;
location / {
root /app;
index index.html;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}

1532
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

20
package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "template",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"echarts": "^5.4.3",
"element-plus": "^2.3.8",
"vue": "^3.0.4",
"vue-router": "^4.2.4",
"vuex": "^4.0.2"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vue/compiler-sfc": "^3.0.4",
"vite": "^4.4.7"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

8
src/App.vue Normal file
View File

@ -0,0 +1,8 @@
<template>
<top-header/>
<router-view style="margin-top: 60px;padding-top: 20px;"/>
</template>
<script setup lang='ts'>
import topHeader from "@/components/topHeader.vue"
</script>

129
src/api/api.ts Normal file
View File

@ -0,0 +1,129 @@
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus'
import router from "@/router/index.js"
// 数据返回的接口
// 定义请求响应参数不含data
// interface Result {
// code: number;
// msg: string
// }
// 请求响应参数包含data
interface ResultData<T = any> {
code: number;
msg: string;
data?: T;
}
const URL: string = 'https://testapi.fecribd.com'
enum RequestEnums {
TIMEOUT = 20000,
OVERDUE = 600, // 登录失效
FAIL = 999, // 请求失败
SUCCESS = 200, // 请求成功
}
const config = {
// 默认地址
baseURL: URL as string,
// 设置超时时间
timeout: RequestEnums.TIMEOUT as number,
// 跨域时候允许携带凭证
withCredentials: false
}
class RequestHttp {
// 定义成员变量并指定类型
service: AxiosInstance;
public constructor(config: AxiosRequestConfig) {
// 实例化axios
this.service = axios.create(config);
/**
*
* -> [] ->
* token校验(token) : token,vuex/pinia/
*/
this.service.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = localStorage.getItem('token') || '';
return {
...config,
headers: {
'authorization': token, // 请求头中携带token信息
}
}
},
(error: AxiosError) => {
// 请求报错
Promise.reject(error)
}
)
/**
*
* -> [] -> JS获取到信息
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
const { data, config } = response; // 解构
if (data.code === RequestEnums.OVERDUE) {
// 登录信息失效应跳转到登录页面并清空本地的token
localStorage.setItem('token', '');
// router.replace({
//   path: '/login'
// })
return Promise.reject(data);
}
// 全局错误信息拦截防止下载文件得时候返回数据流没有code直接报错
if (data.code && data.code !== RequestEnums.SUCCESS) {
ElMessage.error(data); // 此处也可以使用组件提示报错信息
return Promise.reject(data)
}
return data;
},
(error: AxiosError) => {
const { response } = error;
if (response) {
this.handleCode(response)
}
if (!window.navigator.onLine) {
ElMessage.error('网络连接失败');
// 可以跳转到错误页面,也可以不做操作
// return router.replace({
//   path: '/404'
// });
}
}
)
}
handleCode(value:any): void {
switch (value.status) {
case 401:
ElMessage.error('登录失败,请重新登录');
break;
case 403:
ElMessage.error("用户无权限")
router.push({ path: "/login" })
break;
default:
ElMessage.error(value.data.detail);
break;
}
}
// 常用方法封装
get<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.get(url, { params });
}
post<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.post(url, params);
}
put<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.put(url, params);
}
delete<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.delete(url, { params });
}
}
// 导出一个实例对象
export default new RequestHttp(config);

BIN
src/assets/image/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

BIN
src/assets/image/email.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
src/assets/image/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
src/assets/image/上传.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
src/assets/image/文件.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because it is too large Load Diff

1283
src/assets/json/数据.pdf Normal file

File diff suppressed because one or more lines are too long

90
src/components/line.vue Normal file
View File

@ -0,0 +1,90 @@
<template>
<div id="line-container" style="width:100%;height:300px"></div>
</template>
<script setup lang='ts'>
import { onMounted } from "vue"
import * as echarts from 'echarts';
import {
GridComponent,
VisualMapComponent,
MarkLineComponent
} from 'echarts/components';
import { LineChart } from 'echarts/charts';
import { UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([
GridComponent,
VisualMapComponent,
MarkLineComponent,
LineChart,
CanvasRenderer,
UniversalTransition
]);
onMounted(() => {
var chartDom = document.getElementById('line-container');
var myChart = echarts.init(chartDom);
const option = {
xAxis: {
type: 'category',
boundaryGap: false
},
yAxis: {
type: 'value',
boundaryGap: [0, '30%']
},
visualMap: {
type: 'piecewise',
show: false,
dimension: 0,
seriesIndex: 0,
pieces: [
{
gt: 1,
lt: 3,
color: 'rgba(0, 0, 180, 0.4)'
},
{
gt: 5,
lt: 7,
color: 'rgba(0, 0, 180, 0.4)'
}
]
},
series: [
{
type: 'line',
smooth: 0.6,
symbol: 'none',
lineStyle: {
color: '#5470C6',
width: 5
},
markLine: {
symbol: ['none', 'none'],
label: { show: false },
data: [{ xAxis: 1 }, { xAxis: 3 }, { xAxis: 5 }, { xAxis: 7 }]
},
areaStyle: {},
data: [
['2019-10-10', 200],
['2019-10-11', 560],
['2019-10-12', 750],
['2019-10-13', 580],
['2019-10-14', 250],
['2019-10-15', 300],
['2019-10-16', 450],
['2019-10-17', 300],
['2019-10-18', 100]
]
}
]
};
option && myChart.setOption(option);
})
</script>
<style></style>

54
src/components/login.vue Normal file
View File

@ -0,0 +1,54 @@
<template>
<el-dialog v-model="dialogVisible" width="30%" :show-close="false" style="border-radius: 10px;">
<template #header>
<el-row justify="end">
<el-image :src="emailImage" class="header-logo"></el-image>
</el-row>
</template>
<el-row justify="center">
<el-image :src="logo" class="logo"></el-image>
</el-row>
<el-form>
</el-form>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ElMessageBox } from 'element-plus'
import logo from "@/assets/image/logo.png"
import emailImage from "@/assets/image/email.png"
import passwordImage from "@/assets/image/password.png"
const dialogVisible = ref(false)
const handleClose = (done: () => void) => {
ElMessageBox.confirm('Are you sure to close this dialog?')
.then(() => {
done()
})
.catch(() => {
// catch error
})
}
</script>
<style scoped>
.el-dialog {}
.header-logo {
width: 40px;
height: 40px;
}
:deep(.el-dialog__header) {
margin: 0;
padding: 10px;
}
.logo {
width: 200px;
}
</style>

59
src/components/radar.vue Normal file
View File

@ -0,0 +1,59 @@
<template>
<div id="radar-container" style="width:450px;height:450px"></div>
</template>
<script setup lang='ts'>
import { onMounted } from "vue"
import * as echarts from 'echarts';
import { LegendComponent } from 'echarts/components';
import { RadarChart } from 'echarts/charts';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([LegendComponent, RadarChart, CanvasRenderer]);
interface ListItem {
value: number
label: string
}
const props = defineProps<{ data: ListItem[] }>()
onMounted(() => {
var chartDom = document.getElementById('radar-container');
var myChart = echarts.init(chartDom);
const option = {
legend: {
data: ['2023年评级分布'],
orient: 'vertical',
left: 0,
top: 20,
bottom: 0,
},
radar: {
// shape: 'circle',
indicator: props.data.map(item=>{
return {
name:item.label,
max:100
}
})
},
series: [
{
name: 'Budget vs spending',
type: 'radar',
data: [
{
value: props.data.map(item => { return item.value }),
name: '2023年评级分布'
}
]
}
]
};
option && myChart.setOption(option);
})
</script>
<style></style>

19
src/components/table.vue Normal file
View File

@ -0,0 +1,19 @@
<template>
<el-table :data="props.data">
<el-table-column v-for="(item, index) in props.column" :key="index" :label="item.label"
:prop="item.prop"></el-table-column>
</el-table>
</template>
<script setup lang='ts'>
import { ref } from "vue"
interface ListItem {
label: string,
prop: string
}
const props = defineProps<{ column: ListItem[], data: [] }>()
</script>
<style></style>

View File

@ -0,0 +1,68 @@
<template>
<el-header>
<el-row>
<el-link v-for="(item, index) in routerPath" :key="index" @click="goPath(item.path)">{{ item.name }}</el-link>
</el-row>
<el-row align="middle">
<el-link to="/login" v-if="!user.email">登录</el-link>
<el-dropdown trigger="click" v-else>
<span class="el-dropdown-link" style="color:#fff">
你好{{ user.company_name }}
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :icon="Message">{{ user.email }}</el-dropdown-item>
<el-dropdown-item :icon="SwitchButton" divided @click="logOut">退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-row>
<login />
</el-header>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useRouter } from 'vue-router';
import { Message, SwitchButton } from '@element-plus/icons-vue'
import login from "./login.vue"
const routerPath = ref([
{ path: "/home", name: "首页" },
{ path: "/rank", name: "企业榜单" },
{ path: "/rating", name: "参与评级" },
{ path: "/my", name: "我的评级" },
])
const user = ref({ email: "", company_name: "" })
const router = useRouter()
onMounted(() => {
init()
})
const goPath = (value) => {
router.push({ path: value })
}
const init = async () => {
}
const logOut = () => {
}
</script>
<style scoped>
.el-header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: rgba(0, 0, 0, .05);
background-color: #fff;
position: fixed;
width: 100%;
z-index: 999;
top: 0;
}
</style>

11
src/main.js Normal file
View File

@ -0,0 +1,11 @@
import App from '@/App.vue'
import store from "@/store"
import router from "@/router"
import { createApp } from 'vue'
import 'element-plus/dist/index.css'
import ElementPlus from 'element-plus'
import "@/style/public.css"
const app = createApp(App)
app.use(store).use(router).use(ElementPlus).mount('#app')

116
src/pages/home/company.vue Normal file
View File

@ -0,0 +1,116 @@
<template>
<el-select v-model="inputValue" multiple filterable remote placeholder="Enter the enterprise name to query"
:remote-method="remoteMethod" :loading="loading">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
<script setup lang='ts'>
import { onMounted, ref } from "vue"
interface ListItem {
value: string
label: string
}
const inputValue = ref<string>("")
const loading = ref(false)
const list = ref<ListItem[]>([])
const options = ref<ListItem[]>([])
onMounted(() => {
list.value = states.map((item) => {
return { value: `value:${item}`, label: `label:${item}` }
})
})
const remoteMethod = (query: string) => {
if (query) {
loading.value = true
setTimeout(() => {
loading.value = false
options.value = list.value.filter((item) => {
return item.label.toLowerCase().includes(query.toLowerCase())
})
}, 200)
} else {
options.value = []
}
}
const states = [
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Carolina',
'North Dakota',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming',
]
</script>
<style scoped>
.el-input__icon {
font-size: 16px;
}
.el-select {
width: 100%;
}
:deep(.el-input__wrapper) {
border-radius: 25px;
height: 40px;
}
:deep(.el-input__inner) {
padding: 0 20px;
}
:deep(.el-select__input) {
padding: 0 20px;
}
</style>

92
src/pages/home/index.vue Normal file
View File

@ -0,0 +1,92 @@
<template>
<el-container>
<el-row class="container" justify="center">
<h1>中小商企信用评价</h1>
<el-row class="tag-box">
<el-check-tag :checked="!checkValue" @click="checkValue = 0">公司</el-check-tag>
<el-check-tag :checked="checkValue == 1" @click="checkValue = 1">行业</el-check-tag>
<el-check-tag :checked="checkValue == 2" @click="checkValue = 2">全搜索</el-check-tag>
</el-row>
<company v-if="!checkValue" />
<industry v-if="checkValue == 1" />
<el-card shadow="never" v-for="(item, index) in 10">
<el-row align="middle" justify="space-between" style="flex-wrap: nowrap;">
<div class="logo">
</div>
<el-row>
<el-row justify="space-between" style="width:100%">
<el-row align="middle" style="width:auto">
<h5>万科企业股份有限公司</h5><el-tag style="margin-left: 20px;">存续</el-tag>
</el-row>
<p style="font-weight: 800;color: #409EFF;">98/AAA</p>
</el-row>
<el-row>
<span class="describe">法定代表人郁亮</span>
<span class="describe">注册资本1099521.0218万人民币</span>
<span class="describe">成立日期1984-05-30</span>
</el-row>
<el-row>
<span class="describe">电话0755-25609865</span>
<span class="describe">邮箱liufy@vanke.com</span>
</el-row>
<el-row>
<span class="describe">地址深圳市盐田区大梅沙环美路33号万科中心</span>
</el-row>
</el-row>
</el-row>
</el-card>
</el-row>
</el-container>
</template>
<script setup lang='ts'>
import { ref } from "vue"
import company from "./company.vue"
import industry from "./industry.vue"
const checkValue = ref(0)
</script>
<style scoped>
.el-row {
width: 100%;
}
.container {
width: 40%;
margin: 0px auto;
}
.tag-box {
width: 100%;
margin: 20px 0;
}
.logo {
width: 60px;
height: 60px;
background-color: #409EFF;
color: #fff;
font-size: 26px;
font-weight: 800;
text-align: center;
line-height: 60px;
border-radius: 6px;
margin-right: 20px;
}
.el-card {
width: 100%;
margin-top: 20px;
}
h5 {
margin: 5px 0;
font-size: 18px;
}
.describe{
margin:5px 30px 5px 0;
}
</style>

View File

@ -0,0 +1,47 @@
<template>
<el-card shadow="never">
<el-row justify="space-between">
<el-col :span="7">
<el-checkbox v-for="(item, index) in industry.data" :key="index" :label="item.name"></el-checkbox>
</el-col>
<el-col :span="7">
<el-checkbox v-for="(item, index) in industry.data[indexOne].children" :key="index"
:label="item.name"></el-checkbox>
</el-col>
<el-col :span="7">
<el-checkbox v-for="(item, index) in industry.data[indexOne].children[indexTwo].children" :key="index"
:label="item.name"></el-checkbox>
</el-col>
</el-row>
</el-card>
</template>
<script setup lang='ts'>
import { onMounted, ref } from "vue"
import industry from "@/assets/json/industry.json"
const indexOne = ref(0)
const indexTwo = ref(0)
onMounted(() => {
console.log(industry)
})
</script>
<style scoped>
.el-card {
height: 300px;
border-radius: 6px;
}
.el-checkbox {
width: 50%;
}
.el-col {
height: 260px;
overflow-y: auto;
}
</style>

78
src/pages/my/index.vue Normal file
View File

@ -0,0 +1,78 @@
<template>
<div class="table-box">
<el-table :data="data" v-loading="loading" :height="height">
<el-table-column prop="date" label="Date" align="center" />
<el-table-column prop="name" label="Name" align="center" />
<el-table-column prop="state" label="State" align="center" />
<el-table-column prop="city" label="City" align="center" />
<el-table-column prop="address" label="Address" align="center" />
<el-table-column prop="zip" label="Zip" align="center" />
<el-table-column label="operation" align="center">
<template #default="scope">
<el-button type="text"> </el-button>
</template>
</el-table-column>
</el-table>
<el-row justify="end" style="margin-top: 20px;">
<el-pagination :total="count" v-model:current-page="query.page" v-model:page-size="query.page_size"
:page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
@current-change="init" />
</el-row>
</div>
</template>
<script setup lang='ts'>
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router';
const count = ref(400)
const height = ref(0)
const loading = ref(false)
const router = useRouter()
const query = ref({
page: 1,
page_size: 10,
})
const data = ref([
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
])
onMounted(() => {
setTableMaxHeight()
window.addEventListener('resize', () => setTableMaxHeight())
})
function setTableMaxHeight() {
if (document.getElementsByClassName("table-box")[0]) {
height.value = document.getElementsByClassName("table-box")[0].clientHeight
}
}
const init = () => {
}
const showDetail = (value) => {
}
const handleSizeChange = (value) => {
}
</script>
<style scoped>
.table-box {
width: 70%;
height: calc(100vh - 202px);
padding: 16px 0;
margin: 20px auto;
}
</style>

171
src/pages/rank/index.vue Normal file
View File

@ -0,0 +1,171 @@
<template>
<div class="table-box">
<el-table :data="data" v-loading="loading" :height="height">
<el-table-column label="rank" align="center">
<template #default="scope">
{{ scope.$index == 0 ? '🥇' : scope.$index == 1 ? "🥈" : scope.$index == 2 ? "🥉" : scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="date" label="Date" align="center" />
<el-table-column prop="name" label="Name" align="center" />
<el-table-column prop="state" label="State" align="center" />
<el-table-column prop="city" label="City" align="center" />
<el-table-column prop="address" label="Address" align="center" />
<el-table-column prop="zip" label="Zip" align="center" />
<el-table-column label="operation" align="center">
<template #default="scope">
<el-button type="text" @click="showDetail(scope)"> </el-button>
</template>
</el-table-column>
</el-table>
<el-row justify="end" style="margin-top: 20px;">
<el-pagination :total="count" v-model:current-page="query.page" v-model:page-size="query.page_size"
:page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
@current-change="init" />
</el-row>
</div>
</template>
<script setup lang='ts'>
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router';
const count = ref(400)
const height = ref(0)
const loading = ref(false)
const router = useRouter()
const query = ref({
page: 1,
page_size: 10,
})
const data = ref([
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
])
onMounted(() => {
setTableMaxHeight()
window.addEventListener('resize', () => setTableMaxHeight())
})
function setTableMaxHeight() {
if (document.getElementsByClassName("table-box")[0]) {
height.value = document.getElementsByClassName("table-box")[0].clientHeight
}
}
const init = () => {
}
const showDetail = (value) => {
console.log(value)
router.push({path:"/rateDetail"})
}
const handleSizeChange = (value) => {
}
</script>
<style scoped>
.table-box {
width: 70%;
height: calc(100vh - 202px);
padding: 16px 0;
margin: 20px auto;
}
</style>

View File

@ -0,0 +1,90 @@
<template>
<el-row>
<el-check-tag :checked="current == item" v-for="(item, index) in Object.keys(data)" :key="index"
@click="current = item">
{{ item }}
</el-check-tag>
</el-row>
<my-table :data="data[current]" :column="column"></my-table>
</template>
<script setup lang='ts'>
import { ref, onMounted } from "vue"
import myTable from "@/components/table.vue"
const current = ref("资产负债表")
const data = ref({
资产负债表: [
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036',
},
],
利润表: [
],
补充数据表: [
],
})
interface ListItem {
label: string,
prop: string
}
const column = ref<ListItem[]>([])
onMounted(() => {
column.value = Object.keys(data.value['资产负债表'][0]).map(item => {
return { label: item, prop: item }
})
})
</script>
<style></style>

View File

@ -0,0 +1,169 @@
<template>
<div>
<el-row class="container" justify="space-between">
<div class="header-box">
<el-card class="header-card">
<template #header>
<h3>{{ data.base_info.company_name }}</h3>
</template>
<el-row v-for="(key, index) in column" :key="index" justify="space-between">
<span style="font-weight: 800;">{{ index }}</span><span style="width:50%;text-align: right;">{{
data.base_info[key] }}</span>
</el-row>
</el-card>
</div>
<div class="header-box">
<el-card class="header-card">
<template #header>
<el-row justify="space-between">
<h3>评级得分</h3>
<h3 style="color:rgba(127, 131, 247, 1)">{{ data.rating_score }}/{{ data.rating_level }}</h3>
</el-row>
</template>
<el-row>
<h5>该企业评分分数</h5>
<el-slider v-model="data.rating_score" />
</el-row>
<el-row>
<h5>该企业在评级数据池位置</h5>
<el-slider v-model="data.rating_location" />
</el-row>
<el-row>
<h5>该企业评级等级</h5>
<el-slider v-model="data.rating_score" :marks="marks" />
</el-row>
</el-card>
</div>
<div class="header-box">
<el-card class="header-card">
<template #header>
<h3>近年评级结果</h3>
</template>
<div style="width:100%">
<chart-line />
</div>
</el-card>
</div>
</el-row>
<el-row class="container" style="padding-top: 0;width:100%">
<el-card style="width:100%">
<el-tabs v-model="active" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane label="得分分布" name="first">
<score-distribution :data="data.radar_data" />
</el-tab-pane>
<el-tab-pane label="财务报表" name="second">
<financial-table />
</el-tab-pane>
<el-tab-pane label="评级报告" name="third">
<report />
</el-tab-pane>
</el-tabs>
</el-card>
</el-row>
</div>
</template>
<script setup lang='ts'>
import { ref, reactive } from "vue"
import chartLine from "@/components/line.vue"
import scoreDistribution from "./scoreDistribution.vue"
import financialTable from "./financialTable.vue"
import report from "./report.vue"
const marks = reactive({
0: "E",
10: 'D',
20: "C",
30: "CC",
40: "CCC",
50: 'B',
60: "BB",
70: 'BBB',
80: 'A',
90: "AA",
100: "AAA"
})
const active = ref("first")
const data = reactive({
rating_level: "AAA",
rating_score: 93.5,
rating_location: 50,
base_info: {
company_name: "艾江山健康科技蕲春有限公司",
rating_time: "2023-06-15",
area: "四川省-成都市-高新区",
regCapital: "500万人民币",
creditCode: "91421126MA4891YMX5",
socialStaffNum: "110",
regLocation: "蕲春县漕河镇吴庄社区三组美春路25号",
regNumber: "421126000072708",
businessScope: "蕲艾制品生产、销售;蕲艾种植;中药材收购、销售;销售预包装食品、化妆用品、工艺品(不含象牙及其制品)、办公用品、洗涤用品、家用电器、保健用品、电子产品、体育用品;第一类医疗器械零售;提供艾灸咨询服务、电子商务服务(不含许可经营项目)。(涉及许可经营项目,应取得相关部门许可后方可经营)"
},
radar_data: [
{ label: "内部治理", value: 92 },
{ label: "经营规模", value: 100 },
{ label: "财务状况", value: 47.41935483870968 },
{ label: "社会责任", value: 89.25 },
{ label: "守法守规", value: 97.5 }
],
})
const column = {
最新评级时间: "rating_time",
参保人数: "socialStaffNum",
注册资本: "regCapital",
统一社会信用代码: "creditCode",
工商注册号: "regNumber",
地区: "area",
注册地址: "regLocation",
经营范围: "businessScope",
}
const handleClick = () => {
}
</script>
<style scoped>
.container {
padding: 0 20px 20px 20px;
}
:deep(.el-slider__button) {
width: 10px;
height: 10px;
}
.header-box {
width: 32.5%;
}
.header-card {
height: 450px;
}
.header-card>:deep(.el-card__header) {
padding: 0 20px;
}
.header-card>:deep(.el-card__body) {
height: calc(100% - 122px);
display: flex;
align-content: space-around;
flex-wrap: wrap;
overflow: auto;
}
:deep(.el-card__body)>.el-row {
width: 100%;
}
h5 {
margin: 0px 0;
}
:deep(.el-tabs__nav-wrap::after) {
height: 0;
}
</style>

View File

@ -0,0 +1,11 @@
<template>
<embed :src="pdf" style="width:100%;height: 800px;">
</template>
<script setup lang='ts'>
import pdf from "@/assets/json/数据.pdf"
</script>
<style>
</style>

View File

@ -0,0 +1,46 @@
<template>
<el-row>
<el-col :span="8"><chart-radar :data="props.data" /></el-col>
<el-col :span="16">
<el-row style="width: 100%;">
<el-col :span="8" v-for="(item, index) in props.data" :key="index">
<el-progress type="dashboard" :percentage="item.value">
<template #default="{ percentage }">
<h3>{{ percentage.toFixed(2) }}%</h3>
<span>{{ item.label }}</span>
</template>
</el-progress>
</el-col>
</el-row>
</el-col>
</el-row>
</template>
<script setup lang='ts'>
import chartRadar from "@/components/radar.vue";
interface ListItem {
value: number
label: string
}
const props = defineProps<{data:ListItem[]}>()
</script>
<style scoped>
.el-col {
display: flex;
justify-content: center;
align-items: center;
margin: 20px 0;
}
h3 {
margin-bottom: 10px;
}
:deep(.el-progress-circle) {
width: 180px !important;
height: 180px !important;
}
</style>

View File

@ -0,0 +1,87 @@
<template>
<el-container>
<div class="content">
<el-card>
<h3>创建评级</h3>
<el-upload class="upload-demo" drag action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
multiple>
<el-image :src="uploadImage" style="width:100px"></el-image>
<template #tip>
<div class="el-upload__tip">
上传企业 <em>经营报表 (.xlsx)</em>
</div>
</template>
</el-upload>
<!-- <el-card shadow="never" style="margin-bottom: 20px;">
<el-row justify="space-between">
<el-image :src="fileImage" style="width:30px"></el-image>
<div style="width:calc(100% - 50px)">
<span>远东资信评估有限公司2022年年度报表.xlsx</span>
<el-progress :percentage="percentage" />
</div>
</el-row>
</el-card> -->
<el-row justify="end">
<el-button type="primary" disabled> </el-button>
</el-row>
</el-card>
</div>
</el-container>
</template>
<script setup lang='ts'>
import { ref, onMounted, inject } from "vue"
import fileImage from "@/assets/image/文件.png"
import uploadImage from "@/assets/image/上传.png"
const api: any = inject("$axios")
const data = ref({})
const active = ref(0)
const percentage = ref(0)
const init = async () => {
const timer = setInterval(() => {
if (percentage.value < 100) {
percentage.value++
} else {
clearInterval(timer)
}
}, 100)
}
onMounted(() => {
init()
})
const submit = () => {
}
</script>
<style scoped>
.el-container {
width: 100vw;
height: calc(100vh - 60px);
position: absolute;
top: 0;
left: 0;
background: #83a4d4;
background: -webkit-linear-gradient(#83a4d4, #b6fbff);
background: linear-gradient(#83a4d4, #b6fbff);
z-index: -999;
}
.content {
width: 40%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.el-upload__tip {
margin: 20px 0;
}
</style>

34
src/router/index.js Normal file
View File

@ -0,0 +1,34 @@
import { defineAsyncComponent } from "vue";
import { createRouter, createWebHashHistory } from "vue-router";
const pageModules = import.meta.glob('@/pages/*/index.vue')
const reg = /\/src\/pages\/(.+?)\/index\.vue/;
const routes = [
{ path: "/", redirect: "/home" }
]
for (const path in pageModules) {
if (pageModules.hasOwnProperty(path)) {
const routeName = path.match(reg)[1];
if (routeName) {
const module = await pageModules[path]();
routes.push({
path: `/${routeName}`,
component: module.default
});
}
}
}
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL),
routes
});
router.beforeEach((to, from, next) => {
next()
});
export default router;

14
src/store/index.js Normal file
View File

@ -0,0 +1,14 @@
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
}
},
mutations: {
}
})
export default store

9
src/style/element.css Normal file
View File

@ -0,0 +1,9 @@
.el-check-tag{
margin-right: 20px;
font-weight: normal;
font-size: 12px;
}
.el-link{
margin-right: 20px;
}

20
src/style/public.css Normal file
View File

@ -0,0 +1,20 @@
@import url("./element.css");
body {
margin: 0;
background-color: #f6f6f6;
}
span {
font-size: 12px;
}
::-webkit-scrollbar {
width: 5px;
height: 10px;
}
::-webkit-scrollbar-thumb {
background-color: #F0F3F5;
border-radius: 3px;
}

12
vite.config.js Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from "node:url";
export default defineConfig({
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
plugins: [vue()],
});