This commit is contained in:
2025-05-24 01:47:40 +09:00
commit 09d97cbb0b
1594 changed files with 184634 additions and 0 deletions

189
src/pages/[...all].vue Normal file
View File

@@ -0,0 +1,189 @@
<script setup lang="ts">
/**
* This is a Vue Component that will be
* automatically mapped to a catch all path on vue-router (404).
*
* You will be able to access this page at http://localhost:3000/non-existing-page
*
* Read more about routing:
* @see /vite.config.ts
* @see /src/router.ts
*/
import type { VueroSSRContext } from '/@server/types'
import { setResponseHeader, setResponseStatus } from 'h3'
import { useSSRContext } from 'vue'
const { t } = useI18n()
useHead({
title: `${t('pages.not-found.page-title')} - Vuero`,
meta: [
{
name: 'robots',
content: 'noindex',
},
],
})
/**
* When the route is not found, we want to send 404 error code
* We do this by using the `useSSRContext` composable, then we use this to set the 404 status code
* @see src/entry-server.ts
* @see server.ts
*/
if (import.meta.env.SSR) {
const event = useSSRContext<VueroSSRContext>()?.event
if (event) {
setResponseHeader(event, 'Cache-Control', 'no-cache, no-store, must-revalidate')
setResponseStatus(event, 404)
}
}
</script>
<template>
<MinimalLayout>
<div class="error-container">
<div class="error-nav">
<VDarkmodeToggle />
</div>
<div class="error-wrapper">
<div class="error-inner has-text-centered">
<div class="bg-number">
404
</div>
<SVGErrorPlaceholder />
<h3>{{ t('pages.not-found.page-heading') }}</h3>
<p>
{{ t('pages.not-found.page-body') }}
</p>
<div class="button-wrap">
<VButton
color="primary"
elevated
to="/"
>
{{ t('pages.not-found.page-button') }}
</VButton>
</div>
</div>
</div>
</div>
</MinimalLayout>
</template>
<style lang="scss">
.error-container {
width: 100vw;
min-height: 100vh;
.error-nav {
.dark-mode {
position: absolute;
inset-inline-end: 0;
top: 0;
display: inline-block;
transform: scale(0.5);
}
}
.error-wrapper {
max-width: 840px;
margin: 0 auto;
padding-top: 40px;
.error-inner {
position: relative;
max-width: 540px;
margin: 0 auto;
.bg-number {
font-family: var(--font);
position: absolute;
top: -58px;
inset-inline-start: -50px;
inset-inline-end: 0;
margin: 0 auto;
font-size: 28rem;
font-weight: 600;
opacity: 0.15;
z-index: 0;
}
img,
svg,
h3,
p,
.button-wrap {
position: relative;
z-index: 1;
}
img,
.iconify {
display: block;
max-width: 100%;
margin: 0 auto;
}
h3 {
font-size: 1.5rem;
font-family: var(--font-alt);
color: var(--dark-text);
font-weight: 600;
margin-top: 10px;
}
p {
font-family: var(--font);
font-size: 1.1rem;
margin-bottom: 16px;
}
.button-wrap {
.button {
min-width: 220px;
min-height: 50px;
}
}
}
}
}
.is-dark {
.error-container {
.error-wrapper {
.error-inner {
.bg-number {
opacity: 0.09;
}
}
}
}
}
@media only screen and (width <= 767px) {
.error-container {
.error-wrapper {
padding-top: 60px;
.error-inner {
padding: 10px;
.bg-number {
top: -35px;
inset-inline-start: -18px;
inset-inline-end: 0;
font-size: 13rem;
}
img,
.iconify {
max-width: 345px;
}
}
}
}
}
</style>

5
src/pages/auth.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<AuthLayout>
<RouterView />
</AuthLayout>
</template>

856
src/pages/auth/login.vue Normal file
View File

@@ -0,0 +1,856 @@
<script setup lang="ts">
import api from '/@src/utils/api.ts'
import type { UserData } from '/@src/utils/types'
import regex from '/@src/utils/regex.ts'
type StepId = 'login' | 'forgot-password'
const step = ref<StepId>('login')
const isLoading = ref(false)
const privacyModal = ref(false)
const privacy = ref(false)
const loginForm = ref(<UserData>{
bizNo: '999-99-99999',
pwd: 'kospo2025!',
})
const forgetForm = ref(<UserData>{})
const forgetValid = ref({
email: true,
bizNo: true,
})
const router = useRouter()
const notyf = useNotyf()
const token = useUserToken()
const userSession = useUserSession()
const rules = {
bizNo: {
mask: '000-00-00000',
lazy: false,
},
email: {
mask: /^\S*@?\S*$/,
lazy: false,
},
}
const onComplete = (e: any) => {
loginForm.value.bizNo = e.value
}
const onBizNoComplete = (e: any) => {
forgetForm.value.bizNo = e.value
}
const onEmailComplete = (e: any) => {
forgetForm.value.email = e.value
}
const handleLogin = async () => {
if (!isLoading.value) {
isLoading.value = true
api.login(loginForm.value.bizNo, loginForm.value.pwd).then((result: UserData) => {
token.value = result.accessToken
userSession.setUser(result)
notyf.dismissAll()
notyf.primary(`환영합니다. ${result.repNm}`)
router.push('/navbar/dashboards')
isLoading.value = false
}).catch((res) => {
notyf.error(res.response._data.body)
console.log(res.response._data)
if (res.response._data.code === '43003') {
console.log('약관 활성화********')
privacyModal.value = true
}
isLoading.value = false
})
}
}
const forgetSubmit = () => {
let submit = true
notyf.dismissAll()
forgetValid.value = {
bizNo: true,
email: true,
}
if (!regex.bizNo.test(forgetForm.value.bizNo)) {
notyf.error('사업자번호가 정상적이지 않습니다. 다시 입력해주세요.')
forgetValid.value.bizNo = false
submit = false
}
if (!regex.email.test(forgetForm.value.email)) {
notyf.error('이메일이 정상적이지 않습니다. 다시 입력해주세요.')
forgetValid.value.email = false
submit = false
}
if (submit) {
step.value = 'login'
}
else {
return false
}
}
useHead({
title: '소액계약관리 - 로그인',
})
</script>
<template>
<div class="modern-login">
<div class="underlay h-hidden-mobile h-hidden-tablet-p" />
<div class="columns is-gapless is-vcentered">
<div class="column is-relative is-8 h-hidden-mobile h-hidden-tablet-p">
<div class="hero is-fullheight is-image">
<div class="hero-body">
<div class="container">
<div class="columns">
<div class="column">
<img
class="hero-image"
src="/images/illustrations/login/station.svg"
alt=""
>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="column is-4 is-relative">
<div class="top-tools">
<RouterLink
to="/"
class="top-logo"
>
<AnimatedLogo
width="38px"
height="38px"
/>
</RouterLink>
<VDarkmodeToggle />
</div>
<div class="is-form">
<div class="is-form-inner">
<div
class="form-text"
:class="[step !== 'login' && 'is-hidden']"
>
<h2>로그인</h2>
<p>소액 관리 시스템</p>
</div>
<div
class="form-text"
:class="[step === 'login' && 'is-hidden']"
>
<h2>계정 복구</h2>
<p>비밀번호를 초기화 합니다.</p>
</div>
<form
method="post"
novalidate
:class="[step !== 'login' && 'is-hidden']"
class="login-wrapper"
@submit.prevent="handleLogin"
>
<VField>
<VControl icon="lnil lnil-user autv-icon">
<VLabel class="auth-label">
사업자 번호
</VLabel>
<VIMaskInput
class="input v-input"
:mask="Number"
:model-value="loginForm.bizNo"
:options="rules.bizNo"
@complete="onComplete"
placeholder="111-11-11111"
autocomplete="username"
/>
</VControl>
</VField>
<VField>
<VControl icon="lnil lnil-lock-alt autv-icon">
<VLabel class="auth-label">
비밀번호
</VLabel>
<VInput
type="password"
v-model="loginForm.pwd"
placeholder="*********"
autocomplete="current-password"
/>
</VControl>
</VField>
<VField>
<VControl class="is-flex">
<a
tabindex="0"
role="button"
@keydown.enter.prevent="step = 'forgot-password'"
@click="step = 'forgot-password'"
>
비밀번호 찾기
</a>
</VControl>
</VField>
<div class="button-wrap has-help">
<VButton
id="login-button"
:loading="isLoading"
color="primary"
type="submit"
size="big"
rounded
raised
bold
>
확인
</VButton>
<span>
또는
<RouterLink to="/auth/signup">계정</RouterLink>
만드세요.
</span>
</div>
</form>
<form
method="post"
novalidate
:class="[step !== 'forgot-password' && 'is-hidden']"
class="login-wrapper"
@submit.prevent="forgetSubmit"
>
<p class="recover-text">
회원가입시 입력한 이메일주소를 입력해주세요.
임시 비밀번호를 이메일로 보내드리겠습니다.
</p>
<VField>
<VControl icon="lnil lnil-user autv-icon" :class="[!forgetValid.bizNo && 'valid-err']">
<VLabel class="auth-label">
사업자번호
</VLabel>
<VIMaskInput
class="input v-input"
mask="000-00-00000"
:model-value="forgetForm.bizNo"
:options="rules.bizNo"
placeholder="111-11-11111"
autocomplete="username"
@complete="onBizNoComplete"
/>
</VControl>
</VField>
<VField>
<VControl icon="lnil lnil-envelope autv-icon" :class="[!forgetValid.email && 'valid-err']">
<VLabel class="auth-label">
이메일 주소
</VLabel>
<VIMaskInput
class="input v-input"
:mask="String"
:model-value="forgetForm.email"
:options="rules.email"
placeholder="kospo@kospo.co.kr"
autocomplete="email"
@complete="onEmailComplete"
/>
</VControl>
</VField>
<div class="button-wrap">
<VButton
color="white"
size="big"
lower
rounded
@click="step = 'login'"
>
취소
</VButton>
<VButton
color="primary"
size="big"
type="submit"
lower
rounded
solid
>
확인
</VButton>
</div>
</form>
</div>
</div>
</div>
</div>
<VModal
is="form"
:open="privacyModal"
novalidate
title="개인정보 동의서"
actions="right"
cancel-label="취소"
size="big"
@close="privacyModal = false"
>
<template #content>
<Privacy/>
</template>
<template #cancel>
<div class="checkbox-wrap">
<VField horizontal>
<div class="txt">개인정보 이용 수집 동의(필수)</div>
<VCheckbox label="동의" v-model="privacy"/>
</VField>
</div>
</template>
<template #action>
<VButton color="primary">확인</VButton>
</template>
</VModal>
</div>
</template>
<style lang="scss" scoped>
.checkbox-wrap {
.is-horizontal {
justify-content: end;
.txt {
line-height: 40px;
padding-right: 10px;
letter-spacing: -.45px;
}
}
}
.modern-login {
position: relative;
background: var(--white);
min-height: 100vh;
.column {
&.is-relative {
position: relative;
}
}
.hero {
&.has-background-image {
position: relative;
.hero-overlay {
position: absolute;
top: 0;
inset-inline-start: 0;
width: 100%;
height: 100%;
background: #5d4298 !important;
opacity: 0.6;
}
}
}
.underlay {
display: block;
position: absolute;
top: 0;
inset-inline-start: 0;
width: 66.6%;
height: 100%;
background: #fdfdfd;
z-index: 0;
}
.top-tools {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 400px;
margin: 0 auto;
padding: 0 1.25rem;
margin-bottom: 5rem;
.dark-mode {
transform: scale(0.6);
z-index: 2;
}
.top-logo {
display: flex;
justify-content: center;
align-items: center;
z-index: 1;
width: 200px;
img {
display: block;
width: 150px;
//max-width: 50px;
margin: 0 auto;
}
.iconify {
height: 50px;
width: 50px;
}
}
}
.is-image {
position: relative;
border-inline-end: 1px solid var(--fade-grey);
.hero-image {
position: relative;
z-index: 2;
display: block;
margin: -80px auto 0;
max-width: 60%;
width: 60%;
}
}
.is-form {
position: relative;
max-width: 400px;
margin: 0 auto;
form {
animation: fadeInLeft 0.5s;
}
.form-text {
padding: 0 20px;
animation: fadeInLeft 0.5s;
h2 {
font-family: var(--font-alt);
font-weight: 400;
font-size: 3rem;
color: var(--primary);
}
p {
color: var(--muted-grey);
margin-top: 10px;
font-size: 1.5em;
}
}
.recover-text {
font-size: 0.9rem;
color: var(--dark-text);
}
.login-wrapper {
padding: 30px 20px;
.control {
position: relative;
width: 100%;
margin-top: 16px;
.input {
padding-top: 14px;
height: 60px;
border-radius: 10px;
padding-inline-start: 55px;
transition: all 0.3s; // transition-all test
&:focus {
background: color-mix(in oklab, var(--fade-grey), white 6%);
border-color: var(--placeholder);
~ .auth-label,
~ .autv-icon .iconify {
color: var(--muted-grey);
}
}
}
.error-text {
color: var(--danger);
font-size: 0.8rem;
display: none;
padding: 2px 6px;
}
.auth-label {
position: absolute;
top: 6px;
inset-inline-start: 55px;
font-size: 1.0rem;
color: var(--dark-text);
font-weight: 500;
z-index: 2;
transition: all 0.3s; // transition-all test
}
.autv-icon,
:deep(.autv-icon) {
position: absolute;
top: 0;
inset-inline-start: 0;
height: 60px;
width: 60px;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
color: var(--placeholder);
transition: all 0.3s;
}
&.has-validation {
.validation-icon {
position: absolute;
top: 0;
inset-inline-end: 0;
height: 60px;
width: 60px;
display: none;
justify-content: center;
align-items: center;
.icon-wrapper {
height: 20px;
width: 20px;
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--radius-rounded);
.iconify {
height: 10px;
width: 10px;
stroke-width: 3px;
color: var(--white);
}
}
&.is-success {
.icon-wrapper {
background: var(--success);
}
}
&.is-error {
.icon-wrapper {
background: var(--danger);
}
}
}
&.has-success {
.validation-icon {
&.is-success {
display: flex;
}
&.is-error {
display: none;
}
}
}
&.has-error {
.input {
border-color: var(--danger);
}
.error-text {
display: block;
}
.validation-icon {
&.is-error {
display: flex;
}
&.is-success {
display: none;
}
}
}
}
&.is-flex {
display: flex;
align-items: center;
a {
display: block;
margin-inline-start: auto;
color: var(--muted-grey);
font-weight: 500;
font-size: 1.1rem;
transition: color 0.3s;
&:hover,
&:focus {
color: var(--primary);
}
}
.remember-me {
font-size: 0.9rem;
color: var(--muted-grey);
font-weight: 500;
}
}
}
.button-wrap {
margin: 40px 0;
&.has-help {
display: flex;
align-items: center;
> span {
margin-inline-start: 12px;
font-family: var(--font);
a {
color: var(--primary);
font-weight: 500;
padding: 0 2px;
font-size: 1.1em;
}
}
}
.button {
height: 46px;
width: 140px;
margin-inline-start: 6px;
font-size: 1.1em;
&:first-child {
&:hover {
opacity: 0.8;
}
}
}
}
}
}
}
.remember-toggle {
width: 65px;
display: block;
position: relative;
cursor: pointer;
font-size: 22px;
user-select: none;
transform: scale(0.9);
input {
position: absolute;
opacity: 0;
cursor: pointer;
&:checked ~ .toggler {
border-color: var(--primary);
.active,
.inactive {
transform: translateX(calc(var(--transform-direction) * 100%)) rotate(360deg);
}
.active {
opacity: 1;
}
.inactive {
opacity: 0;
}
}
}
.toggler {
position: relative;
display: block;
height: 34px;
width: 61px;
border: 2px solid var(--placeholder);
border-radius: 100px;
transition: all 0.3s; // transition-all test
.active,
.inactive {
position: absolute;
top: 2px;
inset-inline-start: 2px;
height: 26px;
width: 26px;
border-radius: var(--radius-rounded);
background: black;
display: flex;
justify-content: center;
align-items: center;
transform: translateX(calc(var(--transform-direction) * 0))
rotate(calc(var(--transform-direction) * 0));
transition: all 0.3s ease;
.iconify {
color: var(--white);
font-size: 14px;
}
}
.inactive {
background: var(--placeholder);
border-color: var(--placeholder);
opacity: 1;
z-index: 1;
}
.active {
background: var(--primary);
border-color: var(--primary);
opacity: 0;
z-index: 0;
}
}
}
@media only screen and (width <= 767px) {
.modern-login {
.top-logo {
top: 30px;
}
.dark-mode {
top: 36px;
inset-inline-end: 44px;
}
.is-form {
padding-top: 100px;
}
}
}
@media only screen and (width >= 768px) and (width <= 1024px) and (orientation: portrait) {
.modern-login {
.top-logo {
.iconify {
height: 60px;
width: 60px;
}
}
.dark-mode {
top: -58px;
inset-inline-end: 30%;
}
.columns {
display: flex;
height: 100vh;
}
}
}
/* ==========================================================================
Dark mode
========================================================================== */
.is-dark {
.modern-login {
background: var(--dark-sidebar);
.underlay {
background: color-mix(in oklab, var(--dark-sidebar), white 10%);
}
.is-image {
border-color: color-mix(in oklab, var(--dark-sidebar), white 10%);
}
.is-form {
.form-text {
h2 {
color: var(--primary);
}
}
.login-wrapper {
.control {
&.is-flex {
a:hover {
color: var(--primary);
}
}
.input {
background: color-mix(in oklab, var(--dark-sidebar), white 4%);
&:focus {
border-color: var(--primary);
~ .autv-icon {
.iconify {
color: var(--primary);
}
}
}
}
.auth-label {
color: var(--light-text);
}
}
.button-wrap {
&.has-help {
span {
color: var(--light-text);
a {
color: var(--primary);
}
}
}
}
}
}
}
.remember-toggle {
input {
&:checked + .toggler {
border-color: var(--primary);
> span {
background: var(--primary);
}
}
}
.toggler {
border-color: color-mix(in oklab, var(--dark-sidebar), white 12%);
> span {
background: color-mix(in oklab, var(--dark-sidebar), white 12%);
}
}
}
}
</style>

1543
src/pages/auth/signup.vue Normal file

File diff suppressed because it is too large Load Diff

5
src/pages/error.vue Normal file
View File

@@ -0,0 +1,5 @@
<template>
<MinimalLayout>
<RouterView />
</MinimalLayout>
</template>

155
src/pages/error/page-1.vue Normal file
View File

@@ -0,0 +1,155 @@
<script setup lang="ts">
useHead({
title: 'Error Page 1 - Vuero',
})
const router = useRouter()
</script>
<template>
<div class="error-container">
<div class="error-nav">
<VDarkmodeToggle />
</div>
<div class="error-wrapper">
<div class="error-inner has-text-centered">
<div class="bg-number dark-inverted">
404
</div>
<SVGErrorPlaceholder />
<h3 class="dark-inverted">
We couldn't find that page
</h3>
<p>
Looks like we couldn't find that page. Please try again or contact an
administrator if the problem persists.
</p>
<div class="button-wrap">
<VButton
color="primary"
elevated
@click="() => router.go(-1)"
>
Take me Back
</VButton>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.error-container {
width: 100vw;
min-height: 100vh;
.error-nav {
.dark-mode {
position: absolute;
inset-inline-end: 0;
top: 0;
display: inline-block;
transform: scale(0.5);
}
}
.error-wrapper {
max-width: 840px;
margin: 0 auto;
padding-top: 40px;
.error-inner {
position: relative;
max-width: 540px;
margin: 0 auto;
.bg-number {
font-family: var(--font);
position: absolute;
top: -58px;
inset-inline-start: -50px;
inset-inline-end: 0;
margin: 0 auto;
font-size: 28rem;
font-weight: 600;
opacity: 0.15;
z-index: 0;
}
img,
svg,
h3,
p,
.button-wrap {
position: relative;
z-index: 1;
}
img,
.iconify {
display: block;
max-width: 100%;
margin: 0 auto;
}
h3 {
font-size: 1.5rem;
font-family: var(--font-alt);
color: var(--dark-text);
font-weight: 600;
margin-top: 10px;
}
p {
font-family: var(--font);
font-size: 1.1rem;
margin-bottom: 16px;
}
.button-wrap {
.button {
min-width: 220px;
min-height: 50px;
}
}
}
}
}
.is-dark {
.error-container {
.error-wrapper {
.error-inner {
.bg-number {
opacity: 0.09;
}
}
}
}
}
@media only screen and (width <= 767px) {
.error-container {
.error-wrapper {
padding-top: 60px;
.error-inner {
padding: 10px;
.bg-number {
top: -35px;
inset-inline-start: -18px;
inset-inline-end: 0;
font-size: 13rem;
}
img,
.iconify {
max-width: 345px;
}
}
}
}
}
</style>

164
src/pages/error/page-2.vue Normal file
View File

@@ -0,0 +1,164 @@
<script setup lang="ts">
useHead({
title: 'Error Page 2 - Vuero',
})
const router = useRouter()
</script>
<template>
<div class="error-container">
<div class="error-nav">
<VDarkmodeToggle />
</div>
<div class="error-wrapper">
<div class="error-inner has-text-centered">
<div class="bg-number dark-inverted">
404
</div>
<img
class="light-image"
src="/images/illustrations/placeholders/error-2.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/placeholders/error-2-dark.svg"
alt=""
>
<h3 class="dark-inverted">
We couldn't find that page
</h3>
<p>
Looks like we couldn't find that page. Please try again or contact an
administrator if the problem persists.
</p>
<div class="button-wrap">
<VButton
color="primary"
elevated
@click="() => router.go(-1)"
>
Take me Back
</VButton>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.error-container {
width: 100vw;
min-height: 100vh;
.error-nav {
.dark-mode {
position: absolute;
inset-inline-end: 0;
top: 0;
display: inline-block;
transform: scale(0.5);
}
}
.error-wrapper {
max-width: 840px;
margin: 0 auto;
padding-top: 40px;
.error-inner {
position: relative;
max-width: 540px;
margin: 0 auto;
.bg-number {
font-family: var(--font);
position: absolute;
top: -58px;
inset-inline-start: -50px;
inset-inline-end: 0;
margin: 0 auto;
font-size: 28rem;
font-weight: 600;
opacity: 0.15;
z-index: 0;
}
img,
svg,
h3,
p,
.button-wrap {
position: relative;
z-index: 1;
}
img,
.iconify {
display: block;
max-width: 100%;
margin: 0 auto;
}
h3 {
font-size: 1.5rem;
font-family: var(--font-alt);
color: var(--dark-text);
font-weight: 600;
margin-top: 10px;
}
p {
font-family: var(--font);
font-size: 1.1rem;
margin-bottom: 16px;
}
.button-wrap {
.button {
min-width: 220px;
min-height: 50px;
}
}
}
}
}
.is-dark {
.error-container {
.error-wrapper {
.error-inner {
.bg-number {
opacity: 0.09;
}
}
}
}
}
@media only screen and (width <= 767px) {
.error-container {
.error-wrapper {
padding-top: 60px;
.error-inner {
padding: 10px;
.bg-number {
top: -35px;
inset-inline-start: -18px;
inset-inline-end: 0;
font-size: 13rem;
}
img,
.iconify {
max-width: 345px;
}
}
}
}
}
</style>

162
src/pages/error/page-3.vue Normal file
View File

@@ -0,0 +1,162 @@
<script setup lang="ts">
useHead({
title: 'Error Page 3 - Vuero',
})
const router = useRouter()
</script>
<template>
<div class="error-container">
<div class="error-nav">
<VDarkmodeToggle />
</div>
<div class="error-wrapper">
<div class="error-inner has-text-centered">
<img
class="light-image"
src="/images/illustrations/placeholders/error-3.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/placeholders/error-3-dark.svg"
alt=""
>
<h3 class="dark-inverted">
We couldn't find that page
</h3>
<p>
Looks like we couldn't find that page. Please try again or contact an
administrator if the problem persists.
</p>
<div class="button-wrap">
<VButton
color="primary"
elevated
rounded
@click="() => router.go(-1)"
>
Take me Back
</VButton>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.error-container {
width: 100vw;
min-height: 100vh;
.error-nav {
.dark-mode {
position: absolute;
inset-inline-end: 0;
top: 0;
display: inline-block;
transform: scale(0.5);
}
}
.error-wrapper {
max-width: 840px;
margin: 0 auto;
padding-top: 40px;
.error-inner {
position: relative;
max-width: 540px;
margin: 0 auto;
.bg-number {
font-family: var(--font);
position: absolute;
top: -58px;
inset-inline-start: -50px;
inset-inline-end: 0;
margin: 0 auto;
font-size: 28rem;
font-weight: 600;
opacity: 0.15;
z-index: 0;
}
img,
svg,
h3,
p,
.button-wrap {
position: relative;
z-index: 1;
}
img,
.iconify {
display: block;
max-width: 100%;
margin: 0 auto;
}
h3 {
font-size: 1.5rem;
font-family: var(--font-alt);
color: var(--dark-text);
font-weight: 600;
margin-top: 10px;
}
p {
font-family: var(--font);
font-size: 1.1rem;
margin-bottom: 16px;
}
.button-wrap {
.button {
min-width: 220px;
min-height: 50px;
}
}
}
}
}
.is-dark {
.error-container {
.error-wrapper {
.error-inner {
.bg-number {
opacity: 0.09;
}
}
}
}
}
@media only screen and (width <= 767px) {
.error-container {
.error-wrapper {
padding-top: 60px;
.error-inner {
padding: 10px;
.bg-number {
top: -35px;
inset-inline-start: -18px;
inset-inline-end: 0;
font-size: 13rem;
}
img,
.iconify {
max-width: 345px;
}
}
}
}
}
</style>

162
src/pages/error/page-4.vue Normal file
View File

@@ -0,0 +1,162 @@
<script setup lang="ts">
useHead({
title: 'Error Page 4 - Vuero',
})
const router = useRouter()
</script>
<template>
<div class="error-container">
<div class="error-nav">
<VDarkmodeToggle />
</div>
<div class="error-wrapper">
<div class="error-inner has-text-centered">
<img
class="light-image"
src="/images/illustrations/placeholders/error-4.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/placeholders/error-4-dark.svg"
alt=""
>
<h3 class="dark-inverted">
We couldn't find that page
</h3>
<p>
Looks like we couldn't find that page. Please try again or contact an
administrator if the problem persists.
</p>
<div class="button-wrap">
<VButton
color="primary"
elevated
rounded
@click="() => router.go(-1)"
>
Take me Back
</VButton>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.error-container {
width: 100vw;
min-height: 100vh;
.error-nav {
.dark-mode {
position: absolute;
inset-inline-end: 0;
top: 0;
display: inline-block;
transform: scale(0.5);
}
}
.error-wrapper {
max-width: 840px;
margin: 0 auto;
padding-top: 40px;
.error-inner {
position: relative;
max-width: 540px;
margin: 0 auto;
.bg-number {
font-family: var(--font);
position: absolute;
top: -58px;
inset-inline-start: -50px;
inset-inline-end: 0;
margin: 0 auto;
font-size: 28rem;
font-weight: 600;
opacity: 0.15;
z-index: 0;
}
img,
svg,
h3,
p,
.button-wrap {
position: relative;
z-index: 1;
}
img,
.iconify {
display: block;
max-width: 100%;
margin: 0 auto;
}
h3 {
font-size: 1.5rem;
font-family: var(--font-alt);
color: var(--dark-text);
font-weight: 600;
margin-top: 10px;
}
p {
font-family: var(--font);
font-size: 1.1rem;
margin-bottom: 16px;
}
.button-wrap {
.button {
min-width: 220px;
min-height: 50px;
}
}
}
}
}
.is-dark {
.error-container {
.error-wrapper {
.error-inner {
.bg-number {
opacity: 0.09;
}
}
}
}
}
@media only screen and (width <= 767px) {
.error-container {
.error-wrapper {
padding-top: 60px;
.error-inner {
padding: 10px;
.bg-number {
top: -35px;
inset-inline-start: -18px;
inset-inline-end: 0;
font-size: 13rem;
}
img,
.iconify {
max-width: 345px;
}
}
}
}
}
</style>

165
src/pages/error/page-5.vue Normal file
View File

@@ -0,0 +1,165 @@
<script setup lang="ts">
useHead({
title: 'Error Page 5 - Vuero',
})
const router = useRouter()
</script>
<template>
<div class="error-container">
<div class="error-nav">
<VDarkmodeToggle />
</div>
<div class="error-wrapper">
<div class="error-inner has-text-centered">
<div class="bg-number dark-inverted">
500
</div>
<img
class="light-image"
src="/images/illustrations/placeholders/error-5.svg"
alt=""
>
<img
class="dark-image"
src="/images/illustrations/placeholders/error-5-dark.svg"
alt=""
>
<h3 class="dark-inverted">
Internal Server Error
</h3>
<p>
Looks like an unexpacted problem occured. Please try again or contact the
website administrator.
</p>
<div class="button-wrap">
<VButton
color="primary"
elevated
rounded
@click="() => router.go(-1)"
>
Take me Back
</VButton>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.error-container {
width: 100vw;
min-height: 100vh;
.error-nav {
.dark-mode {
position: absolute;
inset-inline-end: 0;
top: 0;
display: inline-block;
transform: scale(0.5);
}
}
.error-wrapper {
max-width: 840px;
margin: 0 auto;
padding-top: 40px;
.error-inner {
position: relative;
max-width: 540px;
margin: 0 auto;
.bg-number {
font-family: var(--font);
position: absolute;
top: -58px;
inset-inline-start: -50px;
inset-inline-end: 0;
margin: 0 auto;
font-size: 28rem;
font-weight: 600;
opacity: 0.15;
z-index: 0;
}
img,
svg,
h3,
p,
.button-wrap {
position: relative;
z-index: 1;
}
img,
.iconify {
display: block;
max-width: 100%;
margin: 0 auto;
}
h3 {
font-size: 1.5rem;
font-family: var(--font-alt);
color: var(--dark-text);
font-weight: 600;
margin-top: 10px;
}
p {
font-family: var(--font);
font-size: 1.1rem;
margin-bottom: 16px;
}
.button-wrap {
.button {
min-width: 220px;
min-height: 50px;
}
}
}
}
}
.is-dark {
.error-container {
.error-wrapper {
.error-inner {
.bg-number {
opacity: 0.09;
}
}
}
}
}
@media only screen and (width <= 767px) {
.error-container {
.error-wrapper {
padding-top: 60px;
.error-inner {
padding: 10px;
.bg-number {
top: -35px;
inset-inline-start: -18px;
inset-inline-end: 0;
font-size: 13rem;
}
img,
.iconify {
max-width: 345px;
}
}
}
}
}
</style>

71
src/pages/index.vue Normal file
View File

@@ -0,0 +1,71 @@
<script setup lang="ts">
import Layout from '/@src/layouts/landing.vue'
import packageJson from '../../package.json'
useHead({
title: '소액계약관리',
})
</script>
<template>
<Layout>
<div class="hero is-fullheight is-active">
<div class="hero-body has-text-centered">
<div class="container">
<h1 class="title is-1 is-bold dark-white is-leading">
Build fast & beautiful web apps with <span>Vuero</span>
</h1>
<h3 class="subtitle is-4">
Premium Vue Dashboard & Webapp UI Kit
<span class="tag is-primary is-rounded">{{ packageJson.version }}</span>
</h3>
<div class="buttons mb-2">
<VButton
href="https://go.cssninja.io/buy-vuero"
color="primary"
rounded
>
Buy Vuero Today
</VButton>
</div>
<div class="trusted-by mb-4">
<span>Trusted by <span>2000+ customers</span></span>
<div class="rating">
<VIcon icon="uiw:star-on" />
<VIcon icon="uiw:star-on" />
<VIcon icon="uiw:star-on" />
<VIcon icon="uiw:star-on" />
<VIcon icon="uiw:star-on" />
</div>
</div>
<div class="hero-mockup-wrap">
<div class="hero-mockup">
<img
class="light-image-block"
src="/images/illustrations/landing/app-1.webp"
alt=""
>
<img
class="dark-image-block"
src="/images/illustrations/landing/app-1-dark.webp"
alt=""
>
</div>
<div class="hero-mockup-gradient" />
</div>
</div>
</div>
</div>
</Layout>
</template>
<style scoped lang="scss">
#vuero-demos,
#vuero-components {
scroll-margin-top: 50px;
}
</style>

3
src/pages/navbar.vue Normal file
View File

@@ -0,0 +1,3 @@
<template>
<RouterView />
</template>

View File

@@ -0,0 +1,33 @@
<route lang="yaml">
meta:
requiresAuth: true
</route>
<script setup lang="ts">
const layoutSwitcher = useLayoutSwitcher()
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = '메인'
})
useHead({
title: '소액계약관리 - 메인',
})
</script>
<template>
<component
:is="(layoutSwitcher.dynamicLayoutComponent as any)"
v-bind="layoutSwitcher.dynamicLayoutProps"
>
<!-- Content Wrapper -->
<RouterView v-slot="{ Component }">
<Transition
name="fade-fast"
mode="out-in"
>
<component :is="Component" />
</Transition>
</RouterView>
</component>
</template>

View File

@@ -0,0 +1,6 @@
<script setup lang="ts">
</script>
<template>
<Dashboard />
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
const router = useRouter()
onMounted(() => {
router.push('/navbar/dashboards')
})
</script>
<template>
<div />
</template>

View File

@@ -0,0 +1,25 @@
<route lang="yaml">
meta:
requiresAuth: true
</route>
<script setup lang="ts">
const layoutSwitcher = useLayoutSwitcher()
</script>
<template>
<component
:is="(layoutSwitcher.dynamicLayoutComponent as any)"
v-bind="layoutSwitcher.dynamicLayoutProps"
>
<!-- Content Wrapper -->
<RouterView v-slot="{ Component }">
<Transition
name="translate-page-x"
mode="out-in"
>
<component :is="Component" />
</Transition>
</RouterView>
</component>
</template>

View File

@@ -0,0 +1,40 @@
<route lang="yaml">
meta:
requiresAuth: true
</route>
<script setup lang="ts">
import { Person } from '/@src/utils/types'
const pageTitle = useVueroContext<string>('page-title')
const approval = ref(null)
onMounted(() => {
pageTitle.value = '신청내역'
})
useHead({
title: '소액계약관리 - 신청내역',
})
const apprLine = ref<any>({})
const onClick = () => {
console.log(apprLine)
}
const onCreateTab = (kind: string) => {
approval.value?.appendTab(kind)
}
</script>
<template>
<div
class="page-content is-relative tabs-wrapper is-slider is-squared is-inverted is-navbar-lg"
>
<VApproval ref="approval" v-model="apprLine"/>
<VButton @click.prevent="onCreateTab('collabo')" color="primary">협조</VButton>
<VButton @click.prevent="onCreateTab('control')" color="dark">통제</VButton>
<VButton @click.prevent="onClick">클릭</VButton>
</div>
</template>

View File

@@ -0,0 +1,24 @@
<route lang="yaml">
meta:
requiresAuth: true
</route>
<script setup lang="ts">
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = '입찰신청'
})
useHead({
title: '소액계약관리 - 입찰신청',
})
</script>
<template>
<div
class="page-content is-relative tabs-wrapper is-slider is-squared is-inverted is-navbar-lg"
>
<Contract />
</div>
</template>

View File

@@ -0,0 +1,24 @@
<route lang="yaml">
meta:
requiresAuth: true
</route>
<script setup lang="ts">
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = '입찰내역'
})
useHead({
title: '소액계약관리 - 입찰내역',
})
</script>
<template>
<div
class="page-content is-relative tabs-wrapper is-slider is-squared is-inverted is-navbar-lg"
>
<Estimate />
</div>
</template>

3
src/pages/sidebar.vue Normal file
View File

@@ -0,0 +1,3 @@
<template>
<RouterView />
</template>

View File

@@ -0,0 +1,27 @@
<route lang="yaml">
meta:
requiresAuth: true
</route>
<script setup lang="ts">
const layoutSwitcher = useLayoutSwitcher()
</script>
<template>
<component
:is="(layoutSwitcher.dynamicLayoutComponent as any)"
v-bind="layoutSwitcher.dynamicLayoutProps"
close-on-change
default-sidebar="dashboard"
>
<!-- Content Wrapper -->
<RouterView v-slot="{ Component }">
<Transition
name="fade-fast"
mode="out-in"
>
<component :is="Component" />
</Transition>
</RouterView>
</component>
</template>

View File

@@ -0,0 +1,12 @@
<script setup lang="ts">
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = '메인'
})
</script>
<template>
<Dashboard />
</template>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
const router = useRouter()
onMounted(() => {
router.push('/sidebar/dashboards')
})
</script>
<template>
<div />
</template>

View File

@@ -0,0 +1,22 @@
<script setup lang="ts">
const layoutSwitcher = useLayoutSwitcher()
</script>
<template>
<component
:is="(layoutSwitcher.dynamicLayoutComponent as any)"
v-bind="layoutSwitcher.dynamicLayoutProps"
close-on-change
default-sidebar="layout"
>
<!-- Content Wrapper -->
<RouterView v-slot="{ Component }">
<Transition
name="translate-page-x"
mode="out-in"
>
<component :is="Component" />
</Transition>
</RouterView>
</component>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import { Person } from '/@src/utils/types'
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = '신청내역'
})
useHead({
title: '소액계약관리 - 신청내역',
})
const apprLine = ref<Person[]>([])
const onClick = () => {
console.log(apprLine.value[0].sabun)
}
</script>
<template>
<div
class="page-content is-relative tabs-wrapper is-slider is-squared is-inverted is-navbar-lg"
>
<!-- default: 일반결재, (코스트 센터 기준 운영부서) control : 통제, (코스트 센터 기준 운영부서) collabo : 협조 -->
<VApproval v-model="apprLine" apprMode="control"/>
<VButton @click.prevent="onClick">클릭</VButton>
</div>
</template>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = '가격조사'
})
useHead({
title: '소액계약관리 - 가격조사',
})
</script>
<template>
<div
class="page-content is-relative tabs-wrapper is-slider is-squared is-inverted is-navbar-lg"
>
<Contract />
</div>
</template>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
const pageTitle = useVueroContext<string>('page-title')
onMounted(() => {
pageTitle.value = '신청내역'
})
useHead({
title: '소액계약관리 - 신청내역',
})
</script>
<template>
<div
class="page-content is-relative tabs-wrapper is-slider is-squared is-inverted is-navbar-lg"
>
<Estimate />
</div>
</template>