This commit is contained in:
2026-04-07 14:50:23 +09:00
commit b4e485502b
4778 changed files with 2017091 additions and 0 deletions

View File

@@ -0,0 +1,153 @@
<template>
<div class="flex flex-col">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div
class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8"
>
<table class="min-w-full divide-y divide-gray-300">
<thead>
<tr>
<th
v-for="(heading, index) in headings"
:key="index"
:width="
heading['widthRatio'] != ''
? heading['widthRatio'] + '%'
: '100%'
"
:class="
index == 0
? 'py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 md:pl-0'
: 'py-3.5 px-3 text-left text-sm font-semibold text-gray-900'
"
scope="col"
>
{{ heading['title'] }}
</th>
<th
v-for="(action, index) in actions"
:key="index + headings.length"
:width="actions.length * 1"
class="relative py-3.5 pl-3 pr-4 sm:pr-6 md:pr-0"
scope="col"
>
<span class="sr-only">{{ action }}</span>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
<tr v-for="(item, itemIndex) in data" :key="itemIndex">
<td
v-for="(heading, index) in headings"
:key="index"
:class="
index == 0
? 'whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 md:pl-0'
: 'whitespace-nowrap py-4 px-3 text-sm text-gray-500'
"
>
{{
columnFilter
? columnFilter(
heading['key'],
item[heading['key']]
)
: item[heading['key']]
}}
</td>
<td
class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 md:pr-0"
>
<a
v-for="(action, index) in actions"
:key="index"
href="javascript:void(0)"
:class="index != 0 ? 'ml-3' : ''"
class="text-indigo-600 hover:text-indigo-900"
@click="doAction(action, item[actionKey])"
>{{ action
}}<span class="sr-only"
>, {{ item['serial'] }}</span
></a
>
</td>
</tr>
<tr v-if="data.length == 0">
<td
:colspan="headings.length"
class="whitespace-nowrap py-4 px-3 text-sm text-gray-500"
>
{{ noDataMessage }}
</td>
</tr>
</tbody>
<tfoot>
<tr>
<th
v-for="(heading, index) in headings"
:key="index"
:width="
heading['widthRatio'] != ''
? heading['widthRatio'] + '%'
: '100%'
"
:class="
index == 0
? 'py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 md:pl-0'
: 'py-3.5 px-3 text-left text-sm font-semibold text-gray-900'
"
scope="col"
>
{{
index == 0
? '합계'
: calcSum(heading['key'])
}}
</th>
<th
v-for="(action, index) in actions"
:key="index + headings.length"
:width="actions.length * 1"
class="relative py-3.5 pl-3 pr-4 sm:pr-6 md:pr-0"
scope="col"
>
<span class="sr-only">{{ action }}</span>
</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
headings: { type: Array<object>, required: true },
actions: { type: Array<string>, default: [] },
data: { type: Array<object>, required: true },
actionKey: { type: String, default: 'serial' },
columnFilter: {
type: Function,
default: (key: string, val: string) => val,
},
doAction: {
type: Function,
default: (key: string) => {
return key;
},
},
noDataMessage: { type: String, default: '조회된 데이터가 없습니다.' },
});
function calcSum(tag) {
let tmpSum = 0;
for (let i = 0; i < props.data.length; i++) {
tmpSum += props.data[i][tag];
}
return tmpSum;
}
</script>

View File

@@ -0,0 +1,96 @@
<template>
<section
class="block fixed bottom-0 inset-x-0 z-50 shadow-lg text-gray-800 bg-gray-700 dark:bg-dark backdrop-blur-lg bg-opacity-30 dark:bg-opacity-30 dark:text-gray-400 border-t-2 border-royal/20"
>
<div id="tabs" class="flex justify-between">
<a
href="#"
class="w-full focus:text-royal hover:text-royal justify-center inline-block text-center pt-2 pb-1 hover:bg-white"
activeClass="dark:text-gray-100 text-black"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 inline-block mb-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
/>
</svg>
<span class="tab block text-xs">Home</span>
</a>
<a
href="#"
class="w-full focus:text-royal hover:text-royal justify-center inline-block text-center pt-2 pb-1 hover:bg-white"
activeClass="dark:text-gray-100 text-black"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 inline-block mb-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"
/>
</svg>
<span class="tab block text-xs">Categories</span>
</a>
<a
href="#"
class="w-full focus:text-royal hover:text-royal justify-center inline-block text-center pt-2 pb-1 hover:bg-white"
activeClass="dark:text-gray-100 text-black"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 inline-block mb-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
<span class="tab block text-xs">Gallery</span>
</a>
<a
href="#"
class="w-full focus:text-royal hover:text-royal justify-center inline-block text-center pt-2 pb-1 hover:bg-white"
activeClass="dark:text-gray-100 text-black"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 inline-block mb-1"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
/>
</svg>
<span class="tab block text-xs">About</span>
</a>
</div>
</section>
</template>
<script setup lang="ts"></script>
<style lang="css" scoped></style>

View File

@@ -0,0 +1,75 @@
import { defineComponent, h, PropType } from 'vue';
import { Bar } from 'vue-chartjs';
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
BarElement,
CategoryScale,
LinearScale,
Plugin,
} from 'chart.js';
ChartJS.register(
Title,
Tooltip,
Legend,
BarElement,
CategoryScale,
LinearScale
);
export default defineComponent({
name: 'BarChart',
components: {
Bar,
},
props: {
chartId: {
type: String,
default: 'bar-chart',
},
chartData: {
type: Object,
},
chartOptions: {
type: Object,
},
width: {
type: Number,
default: 400,
},
height: {
type: Number,
default: 400,
},
cssClasses: {
default: '',
type: String,
},
styles: {
type: Object as PropType<Partial<CSSStyleDeclaration>>,
default: () => {},
},
plugins: {
type: Array as PropType<Plugin<'bar'>[]>,
default: () => [],
},
},
setup(props) {
return () =>
h(Bar, {
chartData: props.chartData,
chartOptions: props.chartOptions,
chartId: props.chartId,
width: props.width,
height: props.height,
cssClasses: props.cssClasses,
styles: props.styles,
plugins: props.plugins,
});
},
});

View File

@@ -0,0 +1,76 @@
import { defineComponent, h, PropType } from 'vue';
import { Line } from 'vue-chartjs';
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale,
Plugin,
} from 'chart.js';
ChartJS.register(
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale
);
export default defineComponent({
name: 'LineChart',
components: {
Line,
},
props: {
chartId: {
type: String,
default: 'line-chart',
},
chartData: {
type: Object,
},
chartOptions: {
type: Object,
},
width: {
type: Number,
default: 400,
},
height: {
type: Number,
default: 400,
},
cssClasses: {
default: '',
type: String,
},
styles: {
type: Object as PropType<Partial<CSSStyleDeclaration>>,
default: () => {},
},
plugins: {
type: Array as PropType<Plugin<'line'>[]>,
default: () => [],
},
},
setup(props) {
return () =>
h(Line, {
chartData: props.chartData,
chartOptions: props.chartOptions,
chartId: props.chartId,
width: props.width,
height: props.height,
cssClasses: props.cssClasses,
styles: props.styles,
plugins: props.plugins,
});
},
});

View File

@@ -0,0 +1,66 @@
import { defineComponent, h, PropType } from 'vue';
import { Pie } from 'vue-chartjs';
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
ArcElement,
CategoryScale,
Plugin,
} from 'chart.js';
ChartJS.register(Title, Tooltip, Legend, ArcElement, CategoryScale);
export default defineComponent({
name: 'PieChart',
components: {
Pie,
},
props: {
chartId: {
type: String,
default: 'pie-chart',
},
chartData: {
type: Object,
},
chartOptions: {
type: Object,
},
width: {
type: Number,
default: 400,
},
height: {
type: Number,
default: 400,
},
cssClasses: {
default: '',
type: String,
},
styles: {
type: Object as PropType<Partial<CSSStyleDeclaration>>,
default: () => {},
},
plugins: {
type: Array as PropType<Plugin<'pie'>[]>,
default: () => [],
},
},
setup(props) {
return () =>
h(Pie, {
chartData: props.chartData,
chartOptions: props.chartOptions,
chartId: props.chartId,
width: props.width,
height: props.height,
cssClasses: props.cssClasses,
styles: props.styles,
plugins: props.plugins,
});
},
});