Commit 4961f4dc by qlintonger xeno

feat: start-compete

parents
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
{
"recommendations": ["Vue.volar"]
}
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useDialog: typeof import('naive-ui')['useDialog']
const useId: typeof import('vue')['useId']
const useLoadingBar: typeof import('naive-ui')['useLoadingBar']
const useMessage: typeof import('naive-ui')['useMessage']
const useModel: typeof import('vue')['useModel']
const useNotification: typeof import('naive-ui')['useNotification']
const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
SampleTable1: typeof import('./src/components/SampleTable-1.vue')['default']
StaticVueGrid: typeof import('./src/components/StaticVueGrid.vue')['default']
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
{
"name": "table2chart",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.3",
"echarts": "^5.6.0",
"lodash": "^4.17.21",
"v-scale-screen": "^2.3.0",
"vue": "^3.4.31",
"vue-grid-layout-v3": "^3.1.2"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"autoprefixer": "^10.4.21",
"naive-ui": "^2.41.0",
"postcss": "^8.5.3",
"tailwindcss": "^4.1.3",
"typescript": "^5.2.2",
"unplugin-auto-import": "^19.1.2",
"unplugin-vue-components": "^28.4.1",
"vfonts": "^0.0.3",
"vite": "^5.3.4",
"vue-tsc": "^2.0.24"
}
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
<script lang="ts" setup>
const layout = [
{x: 0, y: 0, w: 4, h: 1,},
{x: 4, y: 0, w: 8, h: 1,},
{x: 0, y: 2, w: 2, h: 1,},
{x: 2, y: 2, w: 2, h: 1,},
{x: 4, y: 2, w: 3, h: 1,},
{x: 7, y: 1, w: 5, h: 4,},
{x: 0, y: 1, w: 7, h: 1,},
{x: 0, y: 3, w: 7, h: 1,}
].map((a, q) => ({
...a, title: `示例图${q + 1}`, chartData: a.w >= 4 ? [
{type: 'bar', category: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], data: [120, 200, 150, 80, 70, 110, 130]},
{type: 'bar', category: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], data: [120, 200, 150, 80, 70, 110, 130]}
] : [
{
type: 'bar', category: ['Brazil', 'Indonesia', 'USA', 'India', 'China', 'World'], data: [{
name: '2011',
type: 'bar',
data: [18203, 23489, 29034, 104970, 131744, 630230]
},
{
name: '2012',
type: 'bar',
data: [19325, 23438, 31000, 121594, 134141, 681807]
}]
},
]
}));
</script>
<template>
<screen-chart :grid="{cols: 12, rows: 4}" :layout="layout"/>
</template>
<style scoped>
</style>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
\ No newline at end of file
<template>
<v-scale-screen full-screen height="1080" width="1920">
<div class="grid-container w-full h-full">
<div v-for="item in layout" :key="item.i" :style="getItemStyle(item)" class="grid-item">
{{ item.i }}
</div>
</div>
</v-scale-screen>
</template>
<script lang="ts" setup>
import VScaleScreen from 'v-scale-screen';
const layout = [
{x: 0, y: 0, w: 4, h: 1, i: "0"},
{x: 4, y: 0, w: 8, h: 1, i: "1"},
{x: 0, y: 2, w: 2, h: 1, i: "2"},
{x: 2, y: 2, w: 2, h: 1, i: "3"},
{x: 4, y: 2, w: 3, h: 1, i: "4"},
{x: 7, y: 1, w: 5, h: 4, i: "5"},
{x: 0, y: 1, w: 7, h: 1, i: "6"},
{x: 0, y: 3, w: 7, h: 1, i: "7"}
];
function getItemStyle(item: any) {
return {
gridColumn: `${item.x + 1} / span ${item.w}`,
gridRow: `${item.y + 1} / span ${item.h}`,
};
}
</script>
<style scoped>
.grid-container {
display: grid;
grid-template-columns: repeat(12, 1fr); /* 定义 12 列 */
gap: 10px; /* 设置行和列的间距 */
grid-template-rows: repeat(4, 1fr);
}
.grid-item {
background-color: #ccc;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #999;
}
</style>
\ No newline at end of file
<template>
<div ref="chartRef" class="echarts"></div>
</template>
<script setup lang="ts">
import * as echarts from "echarts";
import {ECBasicOption} from "echarts/types/dist/shared";
import {isNumber} from "lodash";
import {computed, nextTick, onMounted, onUnmounted, ref, watch} from "vue";
import {debounce} from "lodash";
interface Props {
options?: ECBasicOption;
width?: string | number;
height?: string | number;
}
const ps = withDefaults(defineProps<Props>(), {
options: () => {
return {};
},
width: "100%",
height: "100%",
});
const getWidth = computed(() => {
if (isNumber(ps.width)) {
return ps.width + "px";
} else {
return ps.width;
}
});
const getHeight = computed(() => {
if (isNumber(ps.height)) {
return ps.height + "px";
} else {
return ps.height;
}
});
const chartRef = ref(null);
let ro: ResizeObserver | null = null
let chartInstance = null as unknown as echarts.ECharts;
let handleR = debounce(function () {
chartInstance?.resize();
}, 500)
onMounted(() => {
const el = chartRef.value;
if (el) {
nextTick(() => {
chartInstance = echarts.init(el);
setOptions(ps.options);
if (!ro) {
ro = new ResizeObserver(handleR);
ro.observe(el, {box: "border-box"});
}
chartInstance?.resize();
});
}
});
onUnmounted(() => {
chartInstance?.dispose();
ro?.disconnect();
ro = null
});
watch(
() => ps.options,
(newVal) => {
setOptions(newVal);
},
{
deep: true,
}
);
const setOptions = (options: ECBasicOption) => {
chartInstance.setOption(options);
};
defineExpose({
getChartInstance: () => chartInstance,
getDom: () => chartRef.value
});
</script>
<style scoped>
.echarts {
width: v-bind(getWidth);
height: v-bind(getHeight);
}
</style>
<script lang="ts" setup>
import {ScreenChartProps} from "./types/ScreenChart.typing.ts";
import {gridItemStyle, mapContainerStyle} from "./funcs/mapStyle.ts";
import GlobalEcharts from "./Components/global-echarts.vue";
import {mapToEchartsOptions} from "./funcs/mapToEchartsOptions.ts";
import {nextTick, onMounted, onUnmounted, ref} from "vue";
import {geoContainer} from "./funcs/mapGEOStyle.ts";
import {debounce} from "lodash";
const props = withDefaults(defineProps<ScreenChartProps>(), {
gap: 16, containerPadding: 12, gridItemPadding: 12
})
const isAllMounted = ref(false)
let ro: ResizeObserver | null = null
onUnmounted(function () {
if (ro) {
ro.disconnect()
ro = null
}
})
const startNow = function calcGEO() {
const rect = containerRef.value.getBoundingClientRect()
geoContainer.value.width = rect.width
geoContainer.value.height = rect.height
}
const calcGEO = debounce(startNow, 500)
onMounted(function () {
nextTick(function () {
isAllMounted.value = true
})
if (!ro) {
ro = new ResizeObserver(calcGEO)
ro.observe(containerRef.value, {box: 'border-box'})
startNow()
}
})
const containerRef = ref()
</script>
<template>
<div :style="mapContainerStyle(props)" class="w-full h-full pr" ref="containerRef">
<div v-for="(item, w) in props.layout" :key="w" :style="gridItemStyle(item, props)" class="flex flex-col">
<div v-if="!item.renderTitle" class="w-full">{{ item.title }}</div>
<component :is="item.renderTitle(item, props.layout)" v-else/>
<div v-if="isAllMounted" class="mt-[12px] w-full flex items-stretch justify-between" style="height: calc(100% - 12px - 12px)">
<global-echarts v-for="(z,x) in item.chartData" :key="x" :options="mapToEchartsOptions(z)"/>
</div>
</div>
</div>
</template>
\ No newline at end of file
import {ref} from "vue";
export const geoContainer = ref({
width: 0, height: 0, designGraphWidth: 1920, designGraphHeight: 1080
})
export function mapWidth(designWidth: number) {
return geoContainer.value.width / geoContainer.value.designGraphWidth * designWidth
}
export function mapWidthStyle(designWidth: number, cssName: string) {
return {
[cssName]: mapWidth(designWidth)
}
}
export function mapHeight(designHeight: number) {
return geoContainer.value.height / geoContainer.value.designGraphHeight * designHeight
}
export function mapHeightStyle(designHeight: number, cssName: string) {
return {
[cssName]: mapHeight(designHeight)
}
}
\ No newline at end of file
import {ScreenChartProps, SingleLayoutConf} from "../types/ScreenChart.typing.ts";
import {mapHeight, mapWidth} from "./mapGEOStyle.ts";
export const gridItemStyle = function (item: SingleLayoutConf, props: ScreenChartProps) {
return {
gridColumn: `${item.x + 1} / span ${item.w}`,
gridRow: `${item.y + 1} / span ${item.h}`,
background: 'red',
boxSizing: 'border-box',
// @ts-ignore
padding: `${mapHeight(props.gridItemPadding)}px ${mapWidth(props.gridItemPadding)}px ${mapHeight(props.gridItemPadding)}px ${mapWidth(props.gridItemPadding)}px`,
fontSize: `${mapWidth(14)}px`
}
}
export const mapContainerStyle = function (props: ScreenChartProps) {
return {
display: 'grid',
// @ts-ignore
gridRowGap: mapHeight(props.gap) + 'px',
// @ts-ignore
gridColumnGap: mapWidth(props.gap) + 'px',
gridTemplateColumns: `repeat(${props.grid.cols}, 1fr)`,
gridTemplateRows: `repeat(${props.grid.rows}, 1fr)`,
boxSizing: 'border-box',
// @ts-ignore
padding: `${mapHeight(props.containerPadding)}px ${mapWidth(props.containerPadding)}px ${mapHeight(props.containerPadding)}px ${mapWidth(props.containerPadding)}px`,
}
}
\ No newline at end of file
import {GeneralChartOptions} from "../types/ScreenChart.typing.ts";
export function mapToEchartsOptions(item: GeneralChartOptions) {
if (item.type === 'bar') {
let series: any[] = []
// 简单类型数据
if (item.data.every(a=>typeof a === 'number')) {
series = [{data: item.data, type:'bar'}]
} else {
series = item.data.map((a) => {
return {
type: 'bar',
name: a.name,
data: a.data
}
})
}
return {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
top: '5%'
},
xAxis: {
type: 'category',
data: item.category,
},
yAxis: {
type: 'value',
boundaryGap: [0, 0.01]
},
series
}
}
}
\ No newline at end of file
import {Plugin} from "vue";
import ScreenChart from "./ScreenChart.vue";
export const injectScreenChart: Plugin = function (app) {
app.component('screen-chart', ScreenChart);
}
\ No newline at end of file
import {VNode} from "vue";
export type BarChartOptions = {
category: string[]
data: number[] | { name: string, data: number[] }[],
type: 'bar'
};
export type GeneralChartOptions = BarChartOptions
export type ChartOptionSet = Array<GeneralChartOptions>
export type SingleLayoutConf = {
x: number, y: number, w: number, h: number, title: string, renderTitle?: (...args: any[]) => VNode,
chartData: ChartOptionSet
}
export type LayoutConfig = Array<SingleLayoutConf>
export type ScreenChartProps = {
layout: LayoutConfig;
gap?: number;
grid: { rows: number, cols: number };
containerPadding?: number;
gridItemPadding?: number;
}
\ No newline at end of file
import {createApp} from 'vue'
import './style.css'
import App from './App.vue'
import {injectScreenChart} from "./lib/Table2Chart/plugin.inject.ts";
createApp(App).use(injectScreenChart).mount('#app')
@import "tailwindcss";
@import "tailwindcss/utilities";
html, body, #app {
display: block;
width: 100%;
height: 100%;
}
\ No newline at end of file
/// <reference types="vite/client" />
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
}
]
}
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true,
"noEmit": true
},
"include": ["vite.config.ts"]
}
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
import Components from 'unplugin-vue-components/vite'
import tailwindcss from '@tailwindcss/vite'
// vite.config.ts
import { defineConfig } from 'vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
tailwindcss(),
AutoImport({
imports: [
'vue',
{
'naive-ui': [
'useDialog',
'useMessage',
'useNotification',
'useLoadingBar'
]
}
]
}),
Components({
resolvers: [NaiveUiResolver()]
})
]
})
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment