sk_fems_ui commit

This commit is contained in:
unknown
2025-07-12 15:13:46 +09:00
commit ffdf5ccb66
380 changed files with 137913 additions and 0 deletions

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

34
.eslintrc.js Normal file
View File

@ -0,0 +1,34 @@
// .eslintrc.js
module.exports = {
// 현재 eslintrc 파일을 기준으로 ESLint 규칙을 적용
root: true,
// 추가적인 규칙들을 적용
extends: [
'eslint:recommended',
'plugin:vue/essential',
'prettier',
'plugin:prettier/recommended',
],
// 코드 정리 플러그인 추가
plugins: ['prettier'],
// 사용자 편의 규칙 추가
rules: {
'prettier/prettier': [
'error',
// 아래 규칙들은 개인 선호에 따라 prettier 문법 적용
// https://prettier.io/docs/en/options.html
{
singleQuote: true,
semi: true,
useTabs: true,
tabWidth: 2,
trailingComma: 'all',
printWidth: 80,
bracketSpacing: true,
arrowParens: 'avoid',
endOfLine: 'auto',
},
],
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
},
};

90
.gitignore vendored Normal file
View File

@ -0,0 +1,90 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp

63
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,63 @@
image: docker:19.03.7
services:
- docker:19.03.7-dind
stages:
- install
- build_docker_image
- build_docker_clear
cache:
paths:
- node_modules/
before_script:
- node -v
- npm install
install_dependencies:
image: node:14.19.3
stage: install
only:
- main
tags:
- test
script:
- echo "=====node install start====="
- pwd
- npm ci
- npm run build
- echo "=====node install end ====="
artifacts:
paths:
- node_modules/
docker-build-main:
variables:
# do not clone again
GIT_STRATEGY: none
stage: build_docker_image
only:
- main
tags:
- test
script:
# make docker image and push to local docker
- echo "=====node build_docker_image start====="
- sudo docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- sudo docker build -t $CI_REGISTRY/root/registry/skfems/ui .
- sudo docker push $CI_REGISTRY/root/registry/skfems/ui
- sudo docker rmi $CI_REGISTRY/root/registry/skfems/ui
- echo "=====node build_docker_image end====="
clear-files:
stage: build_docker_clear
only:
- main
tags:
- test
script:
# 빌드 완료후 빌드시 Root 계정으로 생성된 자료 클리어 처리(아래 부분을 수행 안하면, 다음번 파이브라인 처리시 권한 문제로 수행 안됨)
- sudo rm -rf .nuxt
- sudo rm -rf dist
- sudo rm -rf node_modules

3
.jshintrc Normal file
View File

@ -0,0 +1,3 @@
{
"esversion": 6
}

17
.project Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>sk_fems_ui</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>

7
.settings/.jsdtscope Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
<classpathentry kind="src" path=""/>
<classpathentry kind="output" path=""/>
</classpath>

View File

@ -0,0 +1 @@
org.eclipse.wst.jsdt.launching.JRE_CONTAINER

View File

@ -0,0 +1 @@
Global

14
Dockerfile Normal file
View File

@ -0,0 +1,14 @@
# Package stage
FROM node:14-alpine
WORKDIR /app
ADD dist/ /app/dist/
RUN npm install -g serve
#ENV HOST 0.0.0.0
EXPOSE 3000
ENTRYPOINT ["serve", "-s", "dist", "-p", "3000"]

21
Dockerfile.comm Normal file
View File

@ -0,0 +1,21 @@
# Package stage
FROM node:14-alpine
WORKDIR /app
ADD dist/_nuxt/ /app/dist/_nuxt/
ADD dist/comm/ /app/dist/comm/
ADD dist/login/ /app/dist/login/
COPY dist/*.html /app/dist/
COPY dist/*.ico /app/dist/
COPY dist/*.png /app/dist/
COPY dist/*.svg /app/dist/
COPY dist/.* /app/dist/
RUN npm install -g serve
#ENV HOST 0.0.0.0
EXPOSE 3000
ENTRYPOINT ["serve", "-s", "dist", "-p", "3000"]

21
Dockerfile.comm.auth Normal file
View File

@ -0,0 +1,21 @@
# Package stage
FROM node:14-alpine
WORKDIR /app
ADD dist/_nuxt/ /app/dist/_nuxt/
ADD dist/comm/auth/ /app/dist/comm/auth/
ADD dist/login/ /app/dist/login/
COPY dist/*.html /app/dist/
COPY dist/*.ico /app/dist/
COPY dist/*.png /app/dist/
COPY dist/*.svg /app/dist/
COPY dist/.* /app/dist/
RUN npm install -g serve
#ENV HOST 0.0.0.0
EXPOSE 3000
ENTRYPOINT ["serve", "-s", "dist", "-p", "3000"]

21
Dockerfile.comm.base Normal file
View File

@ -0,0 +1,21 @@
# Package stage
FROM node:14-alpine
WORKDIR /app
ADD dist/_nuxt/ /app/dist/_nuxt/
ADD dist/comm/base/ /app/dist/comm/base/
ADD dist/login/ /app/dist/login/
COPY dist/*.html /app/dist/
COPY dist/*.ico /app/dist/
COPY dist/*.png /app/dist/
COPY dist/*.svg /app/dist/
COPY dist/.* /app/dist/
RUN npm install -g serve
#ENV HOST 0.0.0.0
EXPOSE 3000
ENTRYPOINT ["serve", "-s", "dist", "-p", "3000"]

21
Dockerfile.ems Normal file
View File

@ -0,0 +1,21 @@
# Package stage
FROM node:14-alpine
WORKDIR /app
ADD dist/_nuxt/ /app/dist/_nuxt/
ADD dist/ems/ /app/dist/ems/
ADD dist/login/ /app/dist/login/
COPY dist/*.html /app/dist/
COPY dist/*.ico /app/dist/
COPY dist/*.png /app/dist/
COPY dist/*.svg /app/dist/
COPY dist/.* /app/dist/
RUN npm install -g serve
#ENV HOST 0.0.0.0
EXPOSE 3000
ENTRYPOINT ["serve", "-s", "dist", "-p", "3000"]

21
Dockerfile.ems.base Normal file
View File

@ -0,0 +1,21 @@
# Package stage
FROM node:14-alpine
WORKDIR /app
ADD dist/_nuxt/ /app/dist/_nuxt/
ADD dist/ems/base/ /app/dist/ems/base/
ADD dist/login/ /app/dist/login/
COPY dist/*.html /app/dist/
COPY dist/*.ico /app/dist/
COPY dist/*.png /app/dist/
COPY dist/*.svg /app/dist/
COPY dist/.* /app/dist/
RUN npm install -g serve
#ENV HOST 0.0.0.0
EXPOSE 3000
ENTRYPOINT ["serve", "-s", "dist", "-p", "3000"]

21
Dockerfile.ems.fopm Normal file
View File

@ -0,0 +1,21 @@
# Package stage
FROM node:14-alpine
WORKDIR /app
ADD dist/_nuxt/ /app/dist/_nuxt/
ADD dist/ems/fopm/ /app/dist/ems/fopm/
ADD dist/login/ /app/dist/login/
COPY dist/*.html /app/dist/
COPY dist/*.ico /app/dist/
COPY dist/*.png /app/dist/
COPY dist/*.svg /app/dist/
COPY dist/.* /app/dist/
RUN npm install -g serve
#ENV HOST 0.0.0.0
EXPOSE 3000
ENTRYPOINT ["serve", "-s", "dist", "-p", "3000"]

42
README.md Normal file
View File

@ -0,0 +1,42 @@
# FEMS
## Build Setup
```bash
# install dependencies
$ npm install
# serve with hot reload at localhost:3000
$ npm run dev
# build for production and launch server
$ npm run build
$ npm run start
# generate static project
$ npm run generate
```
For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org).
# VSCode && ESLint Prettier 적용 settings.json 옵션 추가
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.alwaysShowStatus": true,
"eslint.workingDirectories": [
{"mode": "auto"}
],
"eslint.validate": [
"javascript",
"typescript"
],
# 프로젝트 install 후 기동시 버전 mismatch 일때
vue-server-renderer, vue-template-compiler 를 현재 vue 버전에 맞게 수동 인스톨
Ex) npm install vue-server-renderer@2.6.14
Ex) npm install vue-template-compiler@2.6.14
# 프로젝트 Clean 작업
rm package-lock.json; rm .nuxt; rm node_modules;

7
assets/README.md Normal file
View File

@ -0,0 +1,7 @@
# ASSETS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).

View File

@ -0,0 +1,46 @@
@font-face {
font-family: 'Spoqa Han Sans Neo';
font-weight: 700;
src: local('Spoqa Han Sans Neo Bold'),
url('../font/SpoqaHanSansNeo-Bold.woff2') format('woff2'),
url('../font/SpoqaHanSansNeo-Bold.woff') format('woff'),
url('../font/SpoqaHanSansNeo-Bold.ttf') format('truetype');
}
@font-face {
font-family: 'Spoqa Han Sans Neo';
font-weight: 500;
src: local('Spoqa Han Sans Neo Medium'),
url('../font/SpoqaHanSansNeo-Medium.woff2') format('woff2'),
url('../font/SpoqaHanSansNeo-Medium.woff') format('woff'),
url('../font/SpoqaHanSansNeo-Medium.ttf') format('truetype');
}
@font-face {
font-family: 'Spoqa Han Sans Neo';
font-weight: 400;
src: local('Spoqa Han Sans Neo Regular'),
url('../font/SpoqaHanSansNeo-Regular.woff2') format('woff2'),
url('../font/SpoqaHanSansNeo-Regular.woff') format('woff'),
url('../font/SpoqaHanSansNeo-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Spoqa Han Sans Neo';
font-weight: 300;
src: local('Spoqa Han Sans Neo Light'),
url('../font/SpoqaHanSansNeo-Light.woff2') format('woff2'),
url('../font/SpoqaHanSansNeo-Light.woff') format('woff'),
url('../font/SpoqaHanSansNeo-Light.ttf') format('truetype');
}
@font-face {
font-family: 'Spoqa Han Sans Neo';
font-weight: 100;
src: local('Spoqa Han Sans Neo Thin'),
url('../font/SpoqaHanSansNeo-Thin.woff2') format('woff2'),
url('../font/SpoqaHanSansNeo-Thin.woff') format('woff'),
url('../font/SpoqaHanSansNeo-Thin.ttf') format('truetype');
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

BIN
assets/images/login_dm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
assets/images/login_lm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
assets/images/logo_dm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
assets/images/logo_lm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

1314
assets/scss/common.scss Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,75 @@
.v-btn {
background-color: #144985;
&-radius {
&__20 {
border-radius: 20px !important;
}
&__50per {
border-radius: 50% !important;
}
}
&__full {
width: 100%;
}
&__round {
min-width: 100px !important;
padding: 8px 25px !important;
border-radius: 20px !important;
}
&__excel {
background-color: #47535c !important;
}
&-bg {
&__transparent {
background-color: transparent !important;
}
&__blue {
background-color: $--color-primary__blue;
}
&__white-blue {
background-color: $--color-white;
color: $--color-primary__blue;
}
}
&__transparent {
border: 0;
background-color: transparent !important;
}
&.v-btn--icon.v-btn--tile {
border-radius: 4px;
}
}
@each $theme in dark, light {
@include theme($theme);
.v-application.#{$theme}-mode {
.v-btn-bg__w-g5 {
background-color: $--theme-color-w-g5;
i {
color: $--theme-color-g5-w;
}
}
.v-btn {
background-color: map-deep-get(
$config,
#{$theme},
"v-btn-backgroundColor"
);
color: map-deep-get($color, "white", "0");
&.v-btn--disabled {
opacity: 0.4;
background-color: map-deep-get(
$config,
#{$theme},
"v-btn-backgroundColor"
) !important;
color: map-deep-get($color, "white", "0") !important;
.v-icon {
color: map-deep-get($color, "white", "0") !important;
}
}
}
}
}

View File

@ -0,0 +1,15 @@
.v-card {
height: 100%;
}
@each $theme in dark, light {
// @include theme($theme);
.v-application.#{$theme}-mode {
.v-card {
color: map-deep-get($config, #{$theme}, "card-default-color");
.v-card__subtitle {
color: map-deep-get($config, #{$theme}, "card-subtitle");
}
}
}
}

View File

@ -0,0 +1,52 @@
.ft {
&-size {
&_12 { font-size: 12px !important; }
&_13 { font-size: 13px !important; }
&_14 { font-size: 14px !important; }
&_15 { font-size: 15px !important; }
&_16 { font-size: 16px !important; }
&_20 { font-size: 20px !important; }
&_24 { font-size: 24px !important; }
&_32 { font-size: 32px !important; }
&_40 { font-size: 40px !important; }
}
&-wt {
&_100 { font-weight: 100; }
&_200 { font-weight: 200; }
&_300 { font-weight: 300; }
&_400 { font-weight: 400; }
&_500 { font-weight: 500; }
&_600 { font-weight: 600; }
&_700 { font-weight: 700; }
&_800 { font-weight: 800; }
&_900 { font-weight: 900; }
}
&-clr {
&_g-9 { color: $--color-gray_9; }
&_g-7 { color: $--color-gray_7; }
&_g-c { color: $--color-gray_C; }
&_g-555 { color: $--color-gray_555 }
&_g-999 { color: $--color-gray_999; }
&_g-aaa { color: $--color-gray_aaa; }
&_blue { color: $--color-primary__blue; }
&_green { color: $--color-primary__green; }
&_yellow { color: $--color-sub__yellow; }
&_red { color: $--color-warning__red; }
}
}
@each $theme in dark, light{
@include theme($theme);
.v-application.#{$theme}-mode{
color: $--theme-color-w-g5;
.ft {
&-clr {
&_gc-g9 { color: $--theme-color-gc-g9; }
&_g5-gc { color: $--theme-color-g5-gc; }
&_g7-g9 { color: $--theme-color-g7-g9; }
&_g9-g7 { color: $--theme-color-g9-g7; }
}
}
}
}

View File

@ -0,0 +1,376 @@
.tui-grid {
&-layer-state {
z-index: 5 !important;
}
// &-layer-selection {
// width: calc(100% - 2px) !important;
// }
&-container {
width: 100%;
border-radius: 10px;
}
&-content-area {
width: 100%;
border: 0;
}
// &-body-container {
// width: 100% !important;
// }
// &-table {
// width: 100% !important;
// }
// &-lside-area .tui-grid-body-area {
// margin-right: -11px;
// }
&-lside-area .tui-grid-scrollbar-left-bottom {
display: none;
}
&-rside-area {
.tui-grid-header-area,
.tui-grid-summary-area {
margin-right: $scrollbar-width;
}
}
&-border-line-top,
&-border-line-bottom,
&-border-line-right {
border: 0 !important;
}
&-cell {
border-width: 1px !important;
}
&-cell-header {
border-top: 0;
}
&-cell-header,
&-cell-content,
&-cell.tui-grid-cell-summary {
font-family: "Spoqa Han Sans Neo";
font-size: 0.875rem;
font-weight: 400;
line-height: 1.25rem;
letter-spacing: 0.0178571429em;
}
}
.treeGrid {
.tui-grid {
&-header-area {
display: none;
}
&-cell {
border: 0;
background-color: transparent;
}
}
}
.tui-grid-scrollbar-right-top{
z-index:5;
}
@each $theme in dark, light {
@include theme($theme);
.v-application.#{$theme}-mode {
.tui-grid {
&-container,
&-summary-area {
& ::-webkit-scrollbar {
width: $scrollbar-width !important;
height: $scrollbar-width !important;
-webkit-appearance: initial;
background-color: map-deep-get(
$config,
#{$theme},
"scrollbar-track"
) !important;
border-radius: 3px !important;
}
& ::-webkit-scrollbar-track {
background-color: map-deep-get(
$config,
#{$theme},
"scrollbar-track"
) !important;
}
& ::-webkit-scrollbar-thumb {
width: 50px !important;
height: 50px !important;
background-color: map-deep-get($config, #{$theme}, "scrollbar-thumb");
border-radius: 3px;
}
}
&-rside-area {
background-color: map-deep-get($config, #{$theme}, "scrollbar-track");
}
&-scrollbar-left-bottom {
background-color: map-deep-get($config, #{$theme}, "cardBackground");
border-color: map-deep-get($config, #{$theme}, "cardBackground");
}
&-scrollbar-right-top {
background-color: map-deep-get(
$config,
#{$theme},
"tui-grid-header-backgroundColor"
);
border-left-color: map-deep-get(
$config,
#{$theme},
"tui-grid-border-horziontal-color"
);
border-right-color: map-deep-get(
$config,
#{$theme},
"tui-grid-border-horziontal-color"
);
border-bottom-color: map-deep-get(
$config,
#{$theme},
"tui-grid-border-vertical-color"
);
}
&-scrollbar-right-bottom {
width: $scrollbar-width !important;
height: $scrollbar-width !important;
// display: none !important;
border-color: map-deep-get($config, #{$theme}, "scrollbar-track");
background-color: map-deep-get($config, #{$theme}, "scrollbar-track");
// border: none !important;
// bottom: -1px;
// right: -2px;
box-sizing: border-box;
}
&-scrollbar-frozen-border,
&-scrollbar-y-outer-border,
&-scrollbar-y-inner-border {
background-color: transparent !important;
border-color: transparent !important;
}
&-body-area {
overflow: auto !important;
}
&-container,
&-layer-state,
&-body-area,
&-summary-area,
&-cell {
background-color: map-deep-get(
$config,
#{$theme},
"tui-grid-cell-backgroundColor"
);
border-color: map-deep-get(
$config,
#{$theme},
"tui-grid-border-vertical-color"
);
}
&-cell-summary {
text-align: center;
}
&-header-area,
&-cell-header {
background-color: map-deep-get(
$config,
#{$theme},
"tui-grid-header-backgroundColor"
);
border-color: map-deep-get(
$config,
#{$theme},
"tui-grid-border-vertical-color"
);
color: map-deep-get($config, #{$theme}, "activate");
}
&-row-odd,
&-row-even {
.tui-grid-cell-content {
color: map-deep-get($config, #{$theme}, "tui-grid-cell-color");
}
&:hover {
> .tui-grid-cell {
background-color: map-deep-get(
$config,
#{$theme},
"tui-grid-cell-hover-backgroundColor"
);
.tui-grid-cell-content {
color: map-deep-get($config, #{$theme}, "activate");
}
}
}
}
&-cell {
&.row-insert {
background-color: map-deep-get(
$config,
#{$theme},
"tui-grid-cell-insert-color"
);
}
&.row-modify {
background-color: map-deep-get(
$config,
#{$theme},
"tui-grid-cell-modify-color"
);
}
&.row-removed {
background-color: map-deep-get(
$config,
#{$theme},
"tui-grid-cell-removed-color"
);
}
&.row-disabled {
color: map-deep-get(
$config,
#{$theme},
"tui-grid-cell-disabled-color"
);
.tui-grid-cell-content {
color: map-deep-get($config, #{$theme}, "tui-grid-cell-disabled-color");
}
}
&.row-selected {
background-color: map-deep-get(
$config,
#{$theme},
"tui-grid-cell-selected-color"
);
.tui-grid-cell-content {
color: map-deep-get($config, #{$theme}, "activate");
}
}
}
&-layer-focus-border {
background-color: transparent;
}
&-cell-has-tree {
.tui-grid-cell-content {
padding: 0 !important;
}
}
&-tree-extra-content {
display: flex;
align-items: center;
position: relative;
.tui-grid-tree-depth
{
width: 16px;
height: 16px;
position: relative;
left: 0 !important;
margin-right: 6px;
}
}
&-btn-tree {
width: 16px;
height: 16px;
margin-top: 0;
padding-left: 0;
margin-right: 6px;
top: 0;
left: 0 !important;
i {
margin-top: 0;
}
}
&-tree-icon {
margin-top: 0;
top: 0;
i {
width: 16px;
height: 16px;
margin-left: 0;
background-repeat: no-repeat;
background-position: 0 0;
@if $theme == dark {
background-image: url("data:image/svg+xml,%3Csvg id='ico_tree_item' xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cdefs%3E%3Cstyle%3E .cls-1, .cls-4 %7B fill: none; %7D .cls-1 %7B stroke: %23fff; opacity: 0.3; %7D .cls-2 %7B fill: %23fff; %7D .cls-3 %7B stroke: none; %7D %3C/style%3E%3C/defs%3E%3Cg id='사각형_703' data-name='사각형 703' class='cls-1'%3E%3Crect class='cls-3' width='16' height='16' rx='3'/%3E%3Crect class='cls-4' x='0.5' y='0.5' width='15' height='15' rx='2.5'/%3E%3C/g%3E%3Crect id='사각형_1384' data-name='사각형 1384' class='cls-2' width='8' height='1' rx='0.5' transform='translate(4 4.5)'/%3E%3Crect id='_1386' data-name=' 1386' class='cls-2' width='8' height='1' rx='0.5' transform='translate(4 7.5)'/%3E%3Crect id='_1387' data-name=' 1387' class='cls-2' width='8' height='1' rx='0.5' transform='translate(4 10.5)'/%3E%3C/svg%3E%0A");
} @else {
background-image: url("data:image/svg+xml,%3Csvg id='ico_tree_item' xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cdefs%3E%3Cstyle%3E .cls-2%7Bfill:%23a4aac3%7D %3C/style%3E%3C/defs%3E%3Cg id='사각형_703' data-name='사각형 703' style='stroke:%23a4aac3;fill:none'%3E%3Crect width='16' height='16' rx='3' style='stroke:none'/%3E%3Crect x='.5' y='.5' width='15' height='15' rx='2.5' style='fill:none'/%3E%3C/g%3E%3Crect id='사각형_1384' data-name='사각형 1384' class='cls-2' width='8' height='1' rx='.5' transform='translate(4 4.5)'/%3E%3Crect id='_1386' data-name=' 1386' class='cls-2' width='8' height='1' rx='.5' transform='translate(4 7.5)'/%3E%3Crect id='_1387' data-name=' 1387' class='cls-2' width='8' height='1' rx='.5' transform='translate(4 10.5)'/%3E%3C/svg%3E%0A");
}
}
}
&-tree-button-expand {
.tui-grid-btn-tree {
i {
width: 16px;
height: 16px;
background-position: 0 0;
@if $theme == dark {
background-image: url("data:image/svg+xml,%3Csvg id='btn_tree_item_close' xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cdefs%3E%3Cstyle%3E .cls-1 %7B fill: %230d0f17; stroke: %23fff; opacity: 0.3; %7D .cls-2 %7B fill: %23fff; %7D .cls-3 %7B stroke: none; %7D .cls-4 %7B fill: none; %7D %3C/style%3E%3C/defs%3E%3Cg id='사각형_703' data-name='사각형 703' class='cls-1'%3E%3Crect class='cls-3' width='16' height='16' rx='3'/%3E%3Crect class='cls-4' x='0.5' y='0.5' width='15' height='15' rx='2.5'/%3E%3C/g%3E%3Crect id='사각형_1384' data-name='사각형 1384' class='cls-2' width='8' height='2' rx='1' transform='translate(4 7)'/%3E%3C/svg%3E%0A");
} @else {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cg data-name='사각형 703' style='fill:%23f1f3f9;stroke:%23a4aac3'%3E%3Crect width='16' height='16' rx='3' style='stroke:none'/%3E%3Crect x='.5' y='.5' width='15' height='15' rx='2.5' style='fill:none'/%3E%3C/g%3E%3Crect data-name='사각형 1384' width='8' height='2' rx='1' transform='translate(4 7)' style='fill:%23a4aac3'/%3E%3C/svg%3E ");
}
}
}
}
&-tree-button-collapse {
.tui-grid-btn-tree {
i {
width: 16px;
height: 16px;
background-position: 0 0;
@if $theme == dark {
background-image: url("data:image/svg+xml,%3Csvg id='btn_tree_item_open' xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cdefs%3E%3Cstyle%3E .cls-2%7Bfill:%23fff%7D %3C/style%3E%3C/defs%3E%3Cg id='사각형_703' data-name='사각형 703' style='fill:%230d0f17;stroke:%23fff;opacity:.3'%3E%3Crect width='16' height='16' rx='3' style='stroke:none'/%3E%3Crect x='.5' y='.5' width='15' height='15' rx='2.5' style='fill:none'/%3E%3C/g%3E%3Crect id='사각형_1384' data-name='사각형 1384' class='cls-2' width='8' height='2' rx='1' transform='translate(4 7)'/%3E%3Crect id='_1385' data-name=' 1385' class='cls-2' width='2' height='8' rx='1' transform='translate(7 4)'/%3E%3C/svg%3E%0A");
} @else {
background-image: url("data:image/svg+xml,%3Csvg id='btn_tree_item_open' xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cdefs%3E%3Cstyle%3E .cls-2%7Bfill:%23a4aac3%7D %3C/style%3E%3C/defs%3E%3Cg id='사각형_703' data-name='사각형 703' style='fill:%23f1f3f9;stroke:%23a4aac3'%3E%3Crect width='16' height='16' rx='3' style='stroke:none'/%3E%3Crect x='.5' y='.5' width='15' height='15' rx='2.5' style='fill:none'/%3E%3C/g%3E%3Crect id='사각형_1384' data-name='사각형 1384' class='cls-2' width='8' height='2' rx='1' transform='translate(4 7)'/%3E%3Crect id='_1385' data-name=' 1385' class='cls-2' width='2' height='8' rx='1' transform='translate(7 4)'/%3E%3C/svg%3E ");
}
}
}
}
&-tree-button-expand,
&-tree-button-collapse {
.tui-grid-tree-icon {
display: none;
}
}
&-frozen-border {
background-color: transparent;
}
}
[class*="tui-grid-tree-wrapper"] {
display: flex;
align-items: center;
padding-left: 0 !important;
}
}
}

View File

@ -0,0 +1,257 @@
.v-select__custom {
&.v-text-field.v-text-field--solo:not(.v-text-field--solo-flat)
> .v-input__control
> .v-input__slot {
box-shadow: none;
}
&.v-text-field.v-text-field--solo .v-input__control {
min-height: 36px;
height: 36px;
}
&.v-input input {
min-height: 36px;
height: 36px;
}
.v-input--selection-controls {
margin-top: 0;
padding-top: 0;
}
.v-input__slot {
overflow: hidden;
position: relative;
height: 36px;
}
}
.v-input__custom {
.v-input__slot {
&:before,
&:after {
display: none;
}
}
.v-input__slot {
overflow: hidden;
position: relative;
}
}
.v-text-field .v-input__append-inner,
.v-text-field .v-input__prepend-inner {
align-self: center !important;
}
.v-select__widget {
&.v-text-field.v-text-field--solo:not(.v-text-field--solo-flat)
> .v-input__control
> .v-input__slot {
box-shadow: none;
}
&.v-text-field.v-text-field--solo .v-input__control {
min-height: 30px !important;
height: 30px !important;
}
&.v-input input {
min-height: 30px !important;
height: 30px !important;
}
&.v-text-field--outlined > .v-input__control > .v-input__slot {
align-items: stretch;
min-height: 30px;
}
.v-input--selection-controls {
margin-top: 0;
padding-top: 0;
}
.v-input__slot {
overflow: hidden;
position: relative;
height: 30px !important;
}
}
.size {
&-mini {
width: 110px;
height: 30px;
flex: 0 0 auto;
&.v-text-field.v-text-field--solo .v-input__control {
min-height: 30px;
height: 30px;
}
}
}
.v-text-field > .v-input__control > .v-input__slot:after,
.v-text-field > .v-input__control > .v-input__slot:before {
display: none;
}
.v-input__slot {
margin-bottom: 0 !important;
}
.v-input {
margin-top: 0 !important;
padding-top: 0 !important;
}
.v-input__append-inner {
.v-icon {
color: currentColor !important;
}
}
@each $theme in dark, light {
@include theme($theme);
.v-application.#{$theme}-mode {
.v-input {
border-radius: 4px;
&:not(.v-input--radio-group, .v-input--checkbox) {
.v-input__slot {
background-color: map-deep-get(
$config,
#{$theme},
"v-input-backgroundColor"
);
}
}
.v-input__slot {
fieldset {
color: map-deep-get(
$config,
#{$theme},
"v-input-fieldset-color"
) !important;
}
&:hover {
fieldset {
color: map-deep-get(
$config,
#{$theme},
"v-input-fieldset-hover-color"
) !important;
}
}
.v-input__append-inner {
color: map-deep-get($config, #{$theme}, "v-input-icon-color");
}
}
&--is-readonly {
border-color: map-deep-get(
$config,
#{$theme},
"v-input-readonly-border-color"
);
&:not(.v-input--radio-group, .v-input--checkbox) {
.v-input__slot {
background-color: map-deep-get(
$config,
#{$theme},
"v-input-readonly-backgroundColor"
) !important;
}
}
}
&--is-disabled {
border-color: map-deep-get(
$config,
#{$theme},
"v-input-readonly-border-color"
);
&:not(.v-input--radio-group, .v-input--checkbox) {
.v-input__slot {
background-color: map-deep-get(
$config,
#{$theme},
"v-input-disabled-backgroundColor"
) !important;
}
}
input {
color: map-deep-get($config, #{$theme}, "v-input-disabled-color");
}
}
}
.v-select {
.v-label {
color: map-deep-get($config, #{$theme}, "v-select-label-color");
}
&.v-input--is-disabled {
.v-label {
color: map-deep-get($config, #{$theme}, "v-input-disabled-color");
}
.v-icon.v-icon--disabled {
color: map-deep-get(
$config,
#{$theme},
"v-input-disabled-color"
) !important;
}
.v-select__selection--disabled {
color: map-deep-get(
$config,
#{$theme},
"v-input-disabled-color"
) !important;
}
}
}
.v-radio {
.v-label {
color: map-deep-get($config, #{$theme}, "non-activate");
}
&.v-item--active {
.v-label {
color: map-deep-get($config, #{$theme}, "activate");
}
}
}
}
.v-radio {
.v-icon {
opacity: 0.6;
}
.v-label {
color: map-deep-get($config, #{$theme}, "non-activate");
}
&.v-item--active {
.v-icon {
opacity: 1;
}
.v-label {
color: map-deep-get($config, #{$theme}, "activate");
}
}
}
.v-input--checkbox {
.v-icon {
@if $theme == dark {
color: rgba(255, 255, 255, 0.6);
} @else {
color: #aaaaaa;
}
}
}
.v-textarea{
textarea{
padding: 10px;
}
}
}

View File

@ -0,0 +1,33 @@
.vue-numeric-input {
min-height:36px;
}
.vue-numeric-input .numeric-input {
font-size: 14px;
border-radius: 4px;
height:100%;
border-color: rgba(255, 255, 255, 0.4);
background: rgba(57, 64, 94, 0.3);
color: #fff;
&:hover {
border-color: rgba(255, 255, 255, 1);
}
&:focus {
border-color: rgba(255, 255, 255, 1);
}
&:active {
border-color: rgba(255, 255, 255, 1);
}
}
.vue-numeric-input.updown .btn-decrement .btn-icon {
border-color: #fff transparent #fff !important;
}
.vue-numeric-input.updown .btn-increment .btn-icon{
border-color: transparent transparent #fff !important;
}

View File

@ -0,0 +1,88 @@
// .v-tabs {
// height: 38px;
// flex: 0;
// & + .v-tabs-items {
// height: calc(100% - 38px) !important;
// width: 100%;
// background-color: transparent !important;
// }
// &-bar {
// height: 38px;
// background-color: transparent !important;
// border-bottom: 1px solid $--color-hover_d;
// }
// .v-tab {
// margin: 0 !important;
// }
// }
@each $theme in dark, light {
.v-application.#{$theme}-mode {
.v-tabs {
position: relative;
z-index: 2;
.v-slide-group__wrapper {
overflow: visible !important;
contain: initial !important;
}
.v-tab {
border: 1px solid transparent;
border-radius: 6px 6px 0 0;
border-bottom-color: map-deep-get(
$config,
#{$theme},
"v-tabs-active-border-color"
);
background-color: map-deep-get(
$config,
#{$theme},
"v-tabs-backgroundColor"
);
position: relative;
z-index: 2;
transform: translateY(1px);
letter-spacing: 0;
+ .v-tab {
margin-left: 4px;
}
}
.v-tab--active {
border-top-color: map-deep-get(
$config,
#{$theme},
"v-tabs-active-border-color"
);
border-right-color: map-deep-get(
$config,
#{$theme},
"v-tabs-active-border-color"
);
border-left-color: map-deep-get(
$config,
#{$theme},
"v-tabs-active-border-color"
);
border-bottom-color: map-deep-get(
$config,
#{$theme},
"v-tabs-active-backgroundColor"
);
background-color: map-deep-get(
$config,
#{$theme},
"v-tabs-active-backgroundColor"
);
}
}
.v-tabs-items {
position: relative;
z-index: 1;
border-top: 1px
solid
map-deep-get($config, #{$theme}, "v-tabs-active-border-color");
}
}
}

View File

@ -0,0 +1,98 @@
.txt {
&__bar {
display: flex;
&:before {
content: "";
display: inline-block;
width: 2px;
height: 14px;
background-color: #ccc;
margin-right: 8px;
position: relative;
top: 3px;
}
&.log {
&:before {
background-color: $--color-primary__green;
}
}
}
}
.custom-title-1 {
font-size: 2.5rem; // 40px;
font-weight: 700;
line-height: 1.25;
}
.custom-title-2 {
font-size: 1.75rem; // 28px
font-weight: 700;
line-height: 1.25;
}
.custom-title-2-5 {
font-size: 1.5rem; // 28px
font-weight: 700;
line-height: 1.25;
}
.custom-title-3 {
font-size: 1.15rem; // 28px
font-weight: 700;
line-height: 1.25;
}
.custom-title-4 {
font-size: 1.125rem !important;
font-weight: 700 !important;
line-height: 1.25 !important;
}
.custom-title-6 {
font-size: 1.0rem !important;
font-weight: 700 !important;
line-height: 1.25 !important;
}
.custom-title-8 {
font-size: 0.75rem !important;
font-weight: 700 !important;
line-height: 1.0 !important;
}
.custom-text-1 {
font-size: 1.125rem; // 18px;
line-height: 1.2;
}
.custom-text-2 {
opacity: 0.6;
font-family: SpoqaHanSansNeo;
font-size: 14px;
font-weight: normal;
font-stretch: normal;
font-style: normal;
line-height: 2.17;
letter-spacing: normal;
text-align: right;
color: #fff;
}
.text-color--white-0 {
color: map-deep-get($color, "white", "0") !important;
}
@each $theme in dark, light {
.v-application.#{$theme}-mode {
.text-color--activate {
color: map-deep-get($config, #{$theme}, "activate");
}
.text-color--non-activate {
color: map-deep-get($config, #{$theme}, "non-activate");
}
.text-color--sub {
color: map-deep-get($config, #{$theme}, "text-subcolor");
}
}
}

View File

@ -0,0 +1,21 @@
@function setStyle($map, $object, $style) {
@if map-has-key($map, $object) {
@return map-get(map-get($map, $object), $style);
}
@warn "The key ´#{$object} is not available in the map.";
@return null;
}
/// Map deep get
/// @author Kitty Giraudel
/// @access public
/// @param {Map} $map - Map
/// @param {Arglist} $keys - Key chain
/// @return {*} - Desired value
@function map-deep-get($map, $keys...) {
@each $key in $keys {
$map: map-get($map, $key);
}
@return $map;
}

15
assets/scss/mixin.scss Normal file
View File

@ -0,0 +1,15 @@
@import './var.scss';
@import './functions.scss';
@mixin theme($key) {
$--theme-font-color: #{setStyle($config, $key, fontColor)} !global;
$--theme-bg-page: #{setStyle($config, $key, pageBackground)} !global;
$--theme-bg-card: #{setStyle($config, $key, cardBackground)} !global;
$--theme-hover-color: #{setStyle($config, $key, hover)} !global;
$--theme-button-close-color: #{setStyle($config, $key, btnClose)} !global;
$--theme-color-w-g5: #{setStyle($config, $key, w-g5)} !global;
$--theme-color-g5-w: #{setStyle($config, $key, g5-w)} !global;
$--theme-color-gc-g9: #{setStyle($config, $key, gc-g9)} !global;
$--theme-color-g5-gc: #{setStyle($config, $key, g5-gc)} !global;
$--theme-color-g7-g9: #{setStyle($config, $key, g7-g9)} !global;
$--theme-color-g9-g7: #{setStyle($config, $key, g9-g7)} !global;
}

15
assets/scss/theme.scss Normal file
View File

@ -0,0 +1,15 @@
// @import './var.scss';
// @import './functions.scss';
// @import './mixin.scss';
// @each $theme in dark, light{
// @include theme($theme);
// .v-application.#{$theme}-mode{
// span {
// color: $--theme-font-color;
// }
// }
// }

210
assets/scss/var.scss Normal file
View File

@ -0,0 +1,210 @@
$--color-primary__blue: #278bff !important;
$--color-primary__green: #26bf6b !important;
$--color-sub__yellow: #ff9541 !important;
$--color-warning__red: #ff5050 !important;
$--color-white: #fff !important;
$--color-black: #333 !important;
$--color-gray_C: #cccccc !important;
$--color-gray_9: #95a0a9 !important;
$--color-gray_7: #767d83 !important;
$--color-gray_999: #999 !important;
$--color-gray_555: #555 !important;
$--color-gray_aaa: #aaa !important;
$--color-hover_d: #47535c !important;
$--color-hover_l: #f0f5fc !important;
$--theme-font-color: "";
$--theme-bg-page: "";
$--theme-bg-card: "";
$--theme-hover-color: "";
$--theme-button-close-color: "";
$--theme-color-w-g5: "";
$--theme-color-gc-g9: "";
$--theme-color-g5-gc: "";
$--theme-color-g7-g9: "";
$--theme-color-g9-g7: "";
$scrollbar-width: 11px; // 스크롤 바
$column-spacer: 20px; // 검색 영역 열 간격
$row-spacer: 14px; // 검색 영역 행 간격
$color: (
"black": (
"0": #000,
"1": #111
),
"white": (
"0": #fff
),
"week": (
"sun": #fb5a83,
"sat": #2d8cf6
)
);
$config: (
dark: (
w-g5: $--color-white,
g5-w: $--color-gray_555,
gc-g9: $--color-gray_C,
g5-gc: $--color-gray_555,
g7-g9: $--color-gray_7,
g9-g7: $--color-gray_9,
pageBackground: #23272b,
cardBackground: #242940,
hover: #47535c,
btnClose: #24282c,
scrollbar-track: #2f334a,
scrollbar-thumb: #575b72,
card-default-color: #fff,
card-subtitle: rgba(255, 255, 255, 0.6),
activate: #fff,
non-activate: rgba(255, 255, 255, 0.6),
text-subcolor: rgba(255, 255, 255, 0.6),
border-color: rgba(255, 255, 255, 0.1),
router-header: #1d2133,
router-tab-item: #2d3355,
router-tab-item-active: #18579e,
router-tab-item-color: #fff,
router-tab-item-active-color: #fff,
router-tab-item-icon-color: rgba(255, 255, 255, 0.5),
router-tab-item-icon-active-color: #fff,
router-tab-item-hover-color: #3896ff,
router-tab-slot-end-button-backgroundColor: #144985,
v-btn-backgroundColor: #144985,
v-box: #383f5d,
v-banner-border-color: rgba(255, 255, 255, 0.1),
v-treeview-node-root-backgroundColor: #18579e,
v-treeview-node-root-label-color: #fff,
v-treeview-node-root-label-active-color: #fff,
v-treeview-node-root-icon-color: #fff,
v-treeview-node-root-icon-active-color: #fff,
v-treeview-node-subroot-backgroundColor: #2d3355,
v-treeview-node-label-color: rgba(255, 255, 255, 0.6),
v-treeview-node-label-active-color: #fff,
v-treeview-leaf-active-backgroundColor: rgba(45, 51, 85, 0.5),
v-treeview-leaf-active-color: #3896ff,
v-treeview-icon-color: rgba(255, 255, 255, 0.6),
v-treeview-icon-active-color: #fff,
v-input-backgroundColor: rgba(13, 15, 23, 0.3),
v-input-fieldset-color: rgba(255, 255, 255, 0.32),
v-input-fieldset-hover-color: rgba(255, 255, 255, 1),
v-input-icon-color: #fff,
v-input-readonly-border-color: rgba(255, 255, 255, 0.3),
v-input-readonly-backgroundColor: rgba(57, 64, 94, 0.3),
v-input-disabled-backgroundColor: rgba(57, 64, 94, 0.3),
v-input-disabled-color: rgba(255, 255, 255, 0.2),
v-select-label-color: #fff,
v-calendar-weekday-backgroundColor: #383f5d,
v-calendar-weekday-color: #fff,
v-calendar-weekday-border-color: rgba(255, 255, 255, 0.1),
v-calendar-day-color: #fff,
v-calendar-day-in-not-month-color: rgba(255, 255, 255, 0.05),
v-calendar-is-today-background-color: #2d4571,
tui-grid-header-backgroundColor: #383f5d,
tui-grid-border-horziontal-color: #383f5d,
tui-grid-border-vertical-color: rgba(255, 255, 255, 0.1),
tui-grid-cell-backgroundColor: #242940,
tui-grid-cell-color: #fff,
tui-grid-cell-insert-color: #13636c,
tui-grid-cell-selected-color: #1a4e87,
tui-grid-cell-modify-color: #13636c,
tui-grid-cell-removed-color: #f6637b,
tui-grid-cell-disabled-color: rgb(170, 170, 170),
tui-grid-cell-hover-backgroundColor: #31375b,
v-tabs-items-border-color: rgba(255, 255, 255, 0.7),
v-tabs-backgroundColor: rgba(57, 64, 94, 0.5),
v-tabs-active-backgroundColor: #242940,
v-tabs-active-border-color: rgba(255, 255, 255, 0.7),
v-dialog-card-text-color: #fff,
tui-datepicker-backgroundColor: #0d0f17,
tui-datepicker-border-color: rgba(255, 255, 255, 0.3),
tui-datepicker-selectable-hover-color: #2d3355,
tui-datepicker-selected-color: #1a4e87,
tui-datepicker-calendar-color: #fff,
tui-editor-contents-color: #111,
admin-menu-expanded-list-backgroundColor: #144985
),
light: (
w-g5: $--color-gray_555,
g5-w: $--color-white,
gc-g9: $--color-gray_999,
g5-gc: $--color-gray_C,
g7-g9: $--color-gray_9,
g9-g7: $--color-gray_7,
fontColor: #333,
pageBackground: #ececef,
cardBackground: #fefefe,
hover: #f0f5fc,
btnClose: #f1f0f8,
scrollbar-track: #e9e9e9,
scrollbar-thumb: #bbbbbb,
card-default-color: #111,
card-subtitle: #555,
activate: #111,
non-activate: #555,
text-subcolor: #999,
border-color: #ddd,
router-header: #fff,
router-tab-item: #e1e7f3,
router-tab-item-active: #4777d9,
router-tab-item-color: #111,
router-tab-item-active-color: #fff,
router-tab-item-icon-color: #838aa6,
router-tab-item-icon-active-color: #fff,
router-tab-item-hover-color: #366dbe,
router-tab-slot-end-button-backgroundColor: #3f4d7d,
v-btn-backgroundColor: #4777d9,
v-box: #f0f3fa,
v-banner-border-color: #ddd,
v-treeview-node-root-backgroundColor: #4777d9,
v-treeview-node-root-label-color: #111,
v-treeview-node-root-label-active-color: #fff,
v-treeview-node-root-icon-color: #555,
v-treeview-node-root-icon-active-color: #fff,
v-treeview-node-subroot-backgroundColor: #e1e7f3,
v-treeview-node-label-color: #555,
v-treeview-node-label-active-color: #111,
v-treeview-leaf-active-backgroundColor: #edf1f7,
v-treeview-leaf-active-color: #366dbe,
v-treeview-icon-color: #a4aac3,
v-treeview-icon-active-color: #616885,
v-input-backgroundColor: #ffffff,
v-input-fieldset-color: #b4b8c9,
v-input-fieldset-hover-color: #b4b8c9,
v-input-icon-color: #555,
v-input-readonly-border-color: #b4b8c9,
v-input-readonly-backgroundColor: #f5f5f5,
v-input-disabled-backgroundColor: #eee,
v-input-disabled-color: #bbb,
v-select-label-color: #111,
v-calendar-weekday-backgroundColor: #e0e0e0,
v-calendar-weekday-color: #111,
v-calendar-weekday-border-color: #d4d4d4,
v-calendar-day-color: #111,
v-calendar-day-in-not-month-color: #f8f8f8,
v-calendar-is-today-background-color: #e3eaf3,
tui-grid-header-backgroundColor: #e0e0e0,
tui-grid-border-horziontal-color: #e0e0e0,
tui-grid-border-vertical-color: #d4d4d4,
tui-grid-cell-backgroundColor: #fff,
tui-grid-cell-color: #555,
tui-grid-cell-insert-color: #13636c,
tui-grid-cell-selected-color: #ecf2fa,
tui-grid-cell-modify-color: #e6f5f7,
tui-grid-cell-removed-color: #fddde1,
tui-grid-cell-hover-backgroundColor: #f5f5f5,
v-tabs-items-border-color: #989db1,
v-tabs-backgroundColor: #ddd,
v-tabs-active-backgroundColor: #fff,
v-tabs-active-border-color: #989db1,
v-dialog-card-text-color: #111,
tui-datepicker-backgroundColor: #fff,
tui-datepicker-border-color: #b4b8c9,
tui-datepicker-selectable-hover-color: #e1e7f3,
tui-datepicker-selected-color: #4777d9,
tui-datepicker-calendar-color: #111,
tui-editor-contents-color: #111,
admin-menu-expanded-list-backgroundColor: #3f4d7d
)
);

56
assets/variables.scss Normal file
View File

@ -0,0 +1,56 @@
// Ref: https://github.com/nuxt-community/vuetify-module#customvariables
//
// The variables you want to modify
// $font-size-root: 20px;
$body-font-family: "Spoqa Han Sans Neo", "Roboto", "sans-serif" !default;
$grid-gutter: 10px;
$material-dark: (
"background": #383f5d,
"cards": #242940,
"navigation-drawer": #1a1d2d,
"chips": rgba(255, 255, 255, 0.1)
);
$material-light: (
"background": #f0f3fa,
"cards": #fff,
"navigation-drawer": #fff,
"chips": #eee
);
$treeview-node-padding: 10px;
$treeview-node-height: 40px;
$banner-start-padding: 10px;
$banner-end-padding: 10px;
$banner-y-padding: 12px;
$card-border-radius: 10px;
$card-title-font-size: 1.25rem;
$card-title-font-weight: 700;
$card-title-line-height: 1.5;
$card-subtitle-padding: 20px;
$timeline-dot-small-size: 10px;
$data-table-regular-row-height: 36px;
$input-font-size: 14px;
$input-max-height: 36px;
$text-field-filled-full-width-outlined-slot-min-height: 36px;
$text-field-solo-control-min-height: 36px;
$text-field-line-height: 1.285;
$text-field-enclosed-prepend-append-margin-top: 0;
$text-field-enclosed-details-padding: 0 8px;
$tab-font-size: 1rem; // 16px;
$tab-font-weight: 400;
$tabs-bar-height: 45px;
$tabs-item-padding: 12px;
$list-border-radius: 4px;
$list-padding: 0;
$list-item-min-height: 36px;
$list-item-padding: 0;
$list-item-title-font-size: 0.875rem; // 14px;
$list-item-content-padding: 10px 7px;

79
components/Logo.vue Normal file
View File

@ -0,0 +1,79 @@
<template>
<div class="VueToNuxtLogo">
<div class="Triangle Triangle--two" />
<div class="Triangle Triangle--one" />
<div class="Triangle Triangle--three" />
<div class="Triangle Triangle--four" />
</div>
</template>
<style>
.VueToNuxtLogo {
display: inline-block;
animation: turn 2s linear forwards 1s;
transform: rotateX(180deg);
position: relative;
overflow: hidden;
height: 180px;
width: 245px;
}
.Triangle {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
}
.Triangle--one {
border-left: 105px solid transparent;
border-right: 105px solid transparent;
border-bottom: 180px solid #41b883;
}
.Triangle--two {
top: 30px;
left: 35px;
animation: goright 0.5s linear forwards 3.5s;
border-left: 87.5px solid transparent;
border-right: 87.5px solid transparent;
border-bottom: 150px solid #3b8070;
}
.Triangle--three {
top: 60px;
left: 35px;
animation: goright 0.5s linear forwards 3.5s;
border-left: 70px solid transparent;
border-right: 70px solid transparent;
border-bottom: 120px solid #35495e;
}
.Triangle--four {
top: 120px;
left: 70px;
animation: godown 0.5s linear forwards 3s;
border-left: 35px solid transparent;
border-right: 35px solid transparent;
border-bottom: 60px solid #fff;
}
@keyframes turn {
100% {
transform: rotateX(0deg);
}
}
@keyframes godown {
100% {
top: 180px;
}
}
@keyframes goright {
100% {
left: 70px;
}
}
</style>

209
components/Pagination.vue Normal file
View File

@ -0,0 +1,209 @@
<template>
<div class="ly-pager d-flex">
<v-row class="con-pager" align="center" justify="start">
<!-- <span class="grey--text">{{ $m('CAP.PAGE_ROW', '페이지 행당') }} : </span> -->
<v-menu offset-y>
<template #activator="{ on, attrs }">
<v-btn
dark
text
color="primary"
class="ml-2"
v-bind="attrs"
v-on="on"
>
{{ itemsPerPage }}
<v-icon>mdi-chevron-down</v-icon>
</v-btn>
</template>
<v-list v-if="useLimit">
<v-list-item
v-for="(number, index) in itemsPerPageArray"
:key="index"
@click="changePageLeng(number)"
>
<v-list-item-title>{{ number }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
<v-btn fab small class="mr-1 btn-pager" @click="firstToPage">
<v-icon>mdi-chevron-double-left</v-icon>
</v-btn>
<v-btn fab small class="mr-1 btn-pager" @click="prevPage">
<v-icon>mdi-chevron-left</v-icon>
</v-btn>
<!-- <span class="mr-4 grey--text mr-1"> {{ numberOfPages }} / {{ lastPage }} </span> -->
<span class="mr-4 mr-1"> {{ numberOfPages }} / {{ lastPage }} </span>
<v-btn fab small class="ml-1 btn-pager" @click="nextPage">
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
<v-btn fab small class="mr-1 btn-pager" @click="lastToPage">
<v-icon>mdi-chevron-double-right</v-icon>
</v-btn>
<div class="mr-1 btn-pager">
<v-text-field v-model="moveToPage" type="number" label="page" />
</div>
<v-btn fab small class="mr-1" @click="movePage">
{{ $m('CAP.MOVE', '이동') }}
</v-btn>
</v-row>
<slot name="btn"> </slot>
<!-- <Alert ref="alert" /> -->
</div>
</template>
<script>
export default {
props: {
useLimit: {
type: Boolean,
default: true,
},
totalCount: {
type: Number,
default: 0,
},
pageNum: {
type: Number,
default: 0,
},
limit: {
type: Number,
default: 0,
},
itemsPerPageArray: {
type: Array,
default: () => [20, 50, 100],
},
},
data() {
return {
moveToPage: '',
};
},
computed: {
page: function() {
return this.numberOfPages + '-' + this.lastPage;
},
lastPage: function() {
//grid data가 없을 경우 1 리턴
if (this.totalCount == 0) return 1;
let pageLength = Math.floor(this.totalCount / this.itemsPerPage);
if (this.totalCount % this.itemsPerPage > 0) {
pageLength++;
}
return pageLength;
},
itemsPerPage: function() {
return this.limit;
},
numberOfPages: function() {
return Number(this.pageNum);
},
plusPage: function() {
return Number(this.pageNum) + 1;
},
minusPage: function() {
return Number(this.pageNum) - 1;
},
},
watch: {
totalCount: function(newData) {
return (this.moveToPage = newData > 0 ? this.moveToPage : '');
},
moveToPage: function() {
return (this.moveToPage = this.moveToPage.replaceAll(/[^0-9]/g, ''));
},
},
methods: {
nextPage: function() {
if (this.lastPage >= this.plusPage) {
this.$emit('loadData', this.plusPage, this.itemsPerPage);
}
},
prevPage: function() {
if (0 != this.minusPage) {
this.$emit('loadData', this.minusPage, this.itemsPerPage);
}
},
movePage: function() {
if (0 < this.moveToPage && this.moveToPage <= this.lastPage) {
this.$emit('loadData', Number(this.moveToPage), this.itemsPerPage);
} else {
this.$refs.alert.done = () => {
this.$emit('refresh');
};
this.$refs.alert.open(
this.$m('COD.CNFM', '확인'),
this.$m('CAP.PAGE_MOVE_CNFM', '이동할 페이지 정보를 확인 하세요'),
);
}
},
changePageLeng: function(limit) {
//부모 컴포넌트에서 loadData(데이터 조회) 구현
//this.$emit('loadData', this.pageNum, limit);
// limit 변경 시 1 페이지로 초기화
this.$emit('loadData', 1, limit);
},
firstToPage: function() {
if (0 != this.minusPage) {
this.$emit('loadData', 1, this.itemsPerPage);
}
},
lastToPage: function() {
if (this.lastPage >= this.plusPage) {
this.$emit('loadData', this.lastPage, this.itemsPerPage);
}
},
},
};
</script>
<style lang="scss">
.btn-pager {
margin: 4px;
input {
width: 80px;
}
}
.v-btn--fab.v-size--small {
height: 30px;
width: 30px;
}
.v-text-field__details {
display: none;
}
.con-pager {
margin-top: 12px;
margin-bottom: -12px;
}
.ly-pager {
margin: auto;
}
.v-btn {
margin-left: 4px;
}
.v-btn--text {
margin-right: 6px;
}
// .grey--text {
// margin-left: 16px;
// }
.mr-4 {
margin-left: 14px;
}
.v-btn__content {
color: #ffffff;
}
</style>

7
components/README.md Normal file
View File

@ -0,0 +1,7 @@
# COMPONENTS
**This directory is not required, you can delete it if you don't want to use it.**
The components directory contains your Vue.js Components.
_Nuxt.js doesn't supercharge these components._

View File

@ -0,0 +1,18 @@
<template>
<img class="VuetifyLogo" alt="Vuetify Logo" src="/vuetify-logo.svg" />
</template>
<style>
.VuetifyLogo {
height: 180px;
width: 180px;
transform: rotateY(560deg);
animation: turn 3.5s ease-out forwards 1s;
}
@keyframes turn {
100% {
transform: rotateY(0deg);
}
}
</style>

View File

@ -0,0 +1,181 @@
<template>
<v-menu offset-y nudge-bottom="8" :left="true">
<template v-slot:activator="{ on, attrs }">
<v-btn
v-bind="attrs"
v-on="on"
depressed
:ripple="false"
:class="{ miniVariant }"
:style="btnStyle"
>
<v-icon size="32" :class="{ 'mr-2': !miniVariant }"
>$icoAdminMenu</v-icon
>
<span class="body-1 mr-1">{{ userNm }}</span>
<v-icon>mdi-chevron-down</v-icon>
</v-btn>
<!-- <AlertPopup
ref="alertPop"
v-show='false'
:item="item"
/> -->
</template>
<v-list class="pa-2">
<!-- <v-list-item class="mb-1">-->
<!-- <div class="d-flex align-center">-->
<!-- <v-avatar class="mr-1" size="20">-->
<!-- <v-icon color="white">mdi-account-outline</v-icon>-->
<!-- </v-avatar>-->
<!-- <div class="d-flex flex-column">-->
<!-- <span class="body-2 white&#45;&#45;text">관리자</span>-->
<!-- &lt;!&ndash; <nuxt-link class="d-flex align-center mypage" to="/">-->
<!-- <span class="clr-ccc-aaa">마이페이지</span>-->
<!-- <v-icon class="ico-right">mdi-chevron-right</v-icon>-->
<!-- </nuxt-link> &ndash;&gt;-->
<!-- </div>-->
<!-- </div>-->
<!-- </v-list-item>-->
<v-list-item>
<v-btn @click="pswdChange" small elevation="0">
<v-icon class="mr-1" size="20">mdi-account-outline</v-icon>
<span class="body-2">비밀번호 변경</span>
</v-btn>
</v-list-item>
<v-list-item>
<v-btn @click="logout" small elevation="0">
<v-icon class="mr-1" size="20">mdi-logout</v-icon>
<span class="body-2">로그아웃</span>
</v-btn>
</v-list-item>
</v-list>
</v-menu>
</template>
<script>
import $cookie from 'vue-cookie';
import { mapState } from 'vuex';
// import AlertPopup from "~/components/common/modal/AlertPopup";
export default {
props: {
miniVariant: Boolean,
userNm: {
type: String,
require: true,
},
comId: {
type: String,
require: true,
},
},
components: {
// AlertPopup
//ChangePswdPop
},
data() {
return {
logoutUrl: '/login',
item: {},
};
},
computed: {
...mapState(['isDarkMode']),
btnStyle() {
return this.isDarkMode ? { color: '#fff' } : { color: '#111' };
},
},
methods: {
logout() {
$cookie.delete('FEMS_SESSION');
// alert("정상적으로 로그아웃 되었습니다.");
// this.item={
// label:'로그아웃',
// message:'정상적으로 로그아웃 되었습니다.'
// }
// this.$refs['alertPop'].dialog = true;
this.$nextTick(() => {
window.location.href = this.logoutUrl + '?' + this.comId;
});
},
pswdChange() {
this.$parent.$parent.$parent.$parent.$refs['changePswdPop'].dialog = true;
},
},
mounted() {},
};
</script>
<style lang="scss" scoped>
@import '@/assets/scss/var.scss';
@each $theme in dark, light {
.v-application.#{$theme}-mode {
.mdi-account {
color: map-deep-get(
$config,
#{$theme},
'tui-datepicker-calendar-color'
) !important;
}
.v-list {
background-color: map-deep-get(
$config,
#{$theme},
'admin-menu-expanded-list-backgroundColor'
);
border-color: transparent;
}
}
}
.v-menu__content {
box-shadow: 0 5px 15px 0 rgba(0, 0, 0, 0.3);
}
.v-list {
.v-btn {
padding: 0 !important;
margin-bottom: 0;
color: #fff;
&:hover {
color: #46c0ff;
}
> span {
color: currentColor;
}
}
}
.v-btn {
width: 100%;
min-width: auto !important;
display: flex;
align-items: center;
padding: 0 20px !important;
margin-bottom: 12px;
&,
&:before {
background-color: transparent !important;
}
::v-deep {
.v-btn__content {
justify-content: flex-start;
}
}
&.miniVariant {
::v-deep {
.v-btn__content > .body-1,
.v-btn__content > .mdi-chevron-down {
display: none;
}
}
}
}
</style>

View File

@ -0,0 +1,77 @@
<template>
<div id="btnExeclDownload">
<v-btn class="v-btn__round v-btn__excel" @click="downloadExcelFile">
<v-icon>mdi-microsoft-excel</v-icon>
엑셀
</v-btn>
</div>
</template>
<script>
import XLSX from 'xlsx';
export default {
name: 'btnExeclDownload',
props: {
xlsHeader: {
// JSON 데이터로 만들 시 필요
type: Array,
default: [],
},
xlsRowData: {
// JSON 데이터로 만들 시 필요
type: Array,
default: [],
},
tableId: {
// 성성된 테이블 그대로 쓸 경우 그리드에 id 지정 후 바인딩
type: String,
default: null,
},
fileName: {
// 테이블이 하나일 경우, 현재 활성화 메뉴명을 가져와도 될듯,.
type: String,
required: true,
},
sheetName: {
// 지정된 시트명이 없으면 'Sheet1'
type: String,
default: null,
},
},
data() {
return {
xlsData: [],
};
},
methods: {
setExcelData() {
let tmpData = [];
let tmpMap = {};
this.xlsRowData.map(item => {
this.xlsHeader.map(v => {
return Object.assign(tmpMap, { [v.header]: item[v.name] || '' });
});
tmpData = tmpData.concat(tmpMap);
tmpMap = {};
});
this.xlsData = tmpData;
tmpData = null;
tmpMap = null;
},
async downloadExcelFile() {
if (!this.tableId) await this.setExcelData(); // 들어온 JSON 데이타 가공
const workBook = XLSX.utils.book_new(); // 새 시트 생성
const excelData = this.tableId
? // 테이블 그대로 가져올때
XLSX.utils.table_to_sheet(document.getElementById(this.tableId))
: // JSON 형식으로 가져올때
XLSX.utils.json_to_sheet(this.xlsData);
const sheetName = this.sheetName || null;
XLSX.utils.book_append_sheet(workBook, excelData, sheetName); // 시트 명명, 데이터 지정
XLSX.writeFile(workBook, `${this.fileName}.xlsx`); // 엑셀파일 만듬
},
},
};
</script>

View File

@ -0,0 +1,339 @@
<template>
<div class="custom-vc-calender">
<div class="custom-vc-calender-title text-center" v-if="headerVisible">
<span>{{ selectedYear }} {{ selectedMonth }}</span>
</div>
<vc-calendar
ref="myCalendar"
:attributes="calendarAttributes"
class="custom-calendar"
>
<!-- disable-page-swipe
is-expanded -->
<template v-slot:day-content="{ day, attributes }">
<div class="plusButton" style="overflow:auto">
<!-- <p class="plusButton mr-1" >+</p> -->
<span
:class="['day-label', { 'is-holiday': hldyValues(day.day) }]"
@click="addPlan(day.year, day.month, day.day)"
>{{ day.day }}</span
>
<span v-for="attr in attributes" :key="attr.key" class="day-hldyNm">
{{ attr.customData.title }}
</span>
<div class="">
<p
v-for="attr in attributes"
:key="attr.key"
:class="attr.customData.planColor"
@click="updatePlan(attr.customData.planSeq)"
>
{{ attr.customData.planTitle }}
</p>
</div>
</div>
</template>
</vc-calendar>
<component
ref="planPop"
:is="'PlanPop'"
v-show="false"
:detailList="detailList"
:label="planLabel"
:parentPrgmId="parentPrgmId"
/>
</div>
</template>
<script>
import { mapState } from 'vuex';
import Utility from '~/plugins/utility';
import PlanPop from '@/components/common/modal/PlanPop';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
gridName: {
type: String,
require: true,
},
headerVisible: {
type: Boolean,
default: true,
},
},
computed: {
...mapState({
pageData(state) {
return state.pageData[this.parentPrgmId];
},
}),
gridData() {
return this.pageData[this.gridName].data;
},
planData() {
return this.pageData.planData;
},
calendarDtValue() {
const dt = this.pageData['fromDt'];
return dt;
},
selectedYear() {
return Utility.setFormatDate(this.calendarDtValue, 'YYYY');
//return this.calendarDtValue.split("-")[0];
},
selectedMonth() {
return Utility.setFormatDate(this.calendarDtValue, 'MM');
//return this.calendarDtValue.split("-")[1];
},
hldyValues() {
const filter = this.gridData.filter(
data => data.hldyFg === '1' || data.hldyNm,
);
const map = filter.map(item => {
const dt = item.dt.split(' ')[0];
const dtArr = dt.split('-');
const dd = dtArr[2] * 1;
return dd;
});
return day => {
return map.includes(day);
};
},
calendarAttributes() {
if (this.planData.length > 0) {
let attrArr = [];
this.planData.forEach((item, idx) => {
const dt = item.dt.split(' ')[0];
const dtArr = dt.split('-');
const yy = dtArr[0] * 1;
const mm = dtArr[1] * 1 - 1;
const dd = dtArr[2] * 1;
attrArr.push({
key: idx,
customData: {
title: item.hldyNm,
hldyFg: item.hldyFg,
planTitle: item.planTitle,
planSeq: item.planSeq,
planColor:
// item.endDt < Utility.setFormatDate(new Date(), 'YYYY-MM-DD')
// ? 'grey':
item.planColor,
},
dates: new Date(yy, mm, dd),
});
});
return attrArr;
} else {
return [];
}
},
},
watch: {
calendarDtValue(val) {
// if (val) {
// this.$refs.myCalendar.move(this.calendarDtValue);
// }
if (val) {
const yy = Utility.setFormatDate(this.calendarDtValue, 'YYYY');
const mm = Utility.setFormatDate(this.calendarDtValue, 'MM') - 1;
this.$refs.myCalendar.showPageRange(new Date(yy, mm, 1));
}
},
},
components: {
PlanPop,
},
data() {
return {
detailList: myDetail,
planPopDisableFlag: false,
planLabel: '일정',
};
},
methods: {
addPlan(year, month, day) {
this.planLabel = '일정 등록';
this.$refs['planPop'].popUpAction = 'insert';
// this.$refs['planPop'].strtDt = year + '-' + month + '-' + day;
// this.$refs['planPop'].endDt = year + '-' + month + '-' + day;
// this.$refs['planPop'].strtDt = year + '-' + String(month).padStart(2, '0') + '-' + String(day).padStart(2, '0');
// this.$refs['planPop'].endDt = year + '-' + String(month).padStart(2, '0') + '-' + String(day).padStart(2, '0');
this.$refs['planPop'].strtDt = '';
this.$refs['planPop'].endDt = '';
this.$refs['planPop'].addPlanData = {
year : year,
month : month,
day : day
}
this.$refs['planPop'].blocId = this.pageData.blocMstrList[
this.pageData.blocId
].blocId;
this.$refs['planPop'].dialog = true;
},
updatePlan(val) {
this.$refs['planPop'].popUpAction = 'update';
this.$refs['planPop'].planSeq = val;
this.$refs['planPop'].strtDt = '';
this.$refs['planPop'].endDt = '';
this.$refs['planPop'].blocId = this.pageData.blocMstrList[
this.pageData.blocId
].blocId;
this.$refs['planPop'].dialog = true;
},
},
mounted() {},
};
const myDetail = [];
</script>
<style lang="scss" scoped>
@import '@/assets/scss/var.scss';
@import '@/assets/scss/mixin.scss';
::-webkit-scrollbar {
width: 0px;
}
::-webkit-scrollbar-track {
display: none;
}
.custom-vc-calender {
&-title {
margin-bottom: 20px;
}
}
::v-deep {
.custom-calendar.vc-container {
width: 100%;
background-color: transparent;
border: 0;
}
.vc-header,
.vc-arrows-container {
display: none;
}
.vc-weeks {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: 0;
> div {
flex: 1 0 calc(100% / 7);
}
}
.vc-weekday {
display: flex;
align-items: center;
justify-content: center;
height: 36px;
padding: 0;
}
.vc-day {
height: 10.889vh;
min-height: auto;
.day-label {
font-size: 1rem;
line-height: 1.235;
}
.day-hldyNm {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
max-width: 80%;
float: right;
font-size: 10pt;
}
> div {
position: relative;
height: 100%;
padding: 10px;
border-radius: 0 !important;
}
}
.vc-day.is-not-in-month {
* {
opacity: 1 !important;
}
}
.plusButton:hover > span:first-child {
font-weight: bolder;
cursor: pointer;
}
.red {
//background-color: rgba(229,62,62,var(--bg-opacity));
background-color: #e53e3e !important;
color: #fff;
border-radius: 0.125rem;
font-size: 0.75rem;
line-height: 1.25;
text-align: left;
margin-bottom: 0.25rem;
}
.blue {
//background-color: rgba(66,153,225,var(--bg-opacity));
background-color: #4299e1 !important;
color: #fff;
border-radius: 0.125rem;
font-size: 0.75rem;
line-height: 1.25;
text-align: left;
margin-bottom: 0.25rem;
}
.puple {
//background-color: rgba(102,126,234,var(--bg-opacity));
background-color: #667eea !important;
color: #fff;
border-radius: 0.125rem;
font-size: 0.75rem;
line-height: 1.25;
text-align: left;
margin-bottom: 0.25rem;
}
.green {
//background-color: rgba(56,178,172,var(--bg-opacity));
background-color: #38b2ac !important;
color: #fff;
border-radius: 0.125rem;
font-size: 0.75rem;
line-height: 1.25;
text-align: left;
margin-bottom: 0.25rem;
width: 90%;
}
.orange {
//background-color: rgba(237,137,54,var(--bg-opacity));
background-color: #ed8936 !important;
color: #fff;
border-radius: 0.125rem;
font-size: 0.75rem;
line-height: 1.25;
text-align: left;
margin-bottom: 0.25rem;
}
.pink {
//background-color: rgba(237,100,166,var(--bg-opacity));
background-color: #ed64a6 !important;
color: #fff;
border-radius: 0.125rem;
font-size: 0.75rem;
line-height: 1.25;
text-align: left;
margin-bottom: 0.25rem;
}
.grey {
background-color: #6d6d6d;
color: #fff;
border-radius: 0.125rem;
font-size: 0.75rem;
line-height: 1.25;
text-align: left;
margin-bottom: 0.25rem;
}
}
</style>

409
components/common/Chart.vue Normal file
View File

@ -0,0 +1,409 @@
<template>
<div class="chart-wrap">
<v-chart
class="chart"
:option="chartOption"
ref="VChart"
@click="onClick"
@dblclick="onDblClick"
@click.right="onRightClick"
@legendselectchanged="onLegendSelectChanged"
autoresize
/>
</div>
</template>
<script>
import VChart from 'vue-echarts';
import { mapState } from 'vuex';
export default {
components: {
VChart,
},
props: {
parentPrgmId: {
type: String,
require: true,
},
widgetId: {
type: String,
require: true,
},
widgetData: {
type: String,
require: true,
},
modalId: {
type: String,
require: true,
},
modalDataKey: {
type: String,
require: true,
},
chartName: {
type: String,
require: true,
},
chartColor: {
type: String,
require: true,
},
},
data() {
return {
legendSeletedList: {},
};
},
computed: {
...mapState({
isDarkMode: 'isDarkMode',
chartOption(state) {
var dark_Col = [
'#01AE6A',
'#FFB046',
'#F6637B',
'#944FE9',
'#4385E3',
'#00AA8C',
'#FF8808',
'#EA5E9A',
'#B742D9',
'#6363DA',
'#79B100',
'#D66500',
'#DC5ABC',
'#764FD7',
'#009DD1',
'#3BAD43',
'#D75E3D',
'#CF4DCA',
'#A148D9',
'#5972DF',
];
var darkCol_1_5 = [
'#01AE6A',
'#089362',
'#0F7959',
'#165E51',
'#19514D',
];
var darkCol_1_10 = [
'#01AE6A',
'#04A166',
'#089362',
'#0B865D',
'#0F7959',
'#126C55',
'#165E51',
'#19514D',
'#1D4448',
'#1F3D46',
];
var darkCol_2_5 = [
'#FFB046',
'#D39545',
'#A77A44',
'#7C5F42',
'#665242',
];
var darkCol_2_10 = [
'#FFB046',
'#EAA345',
'#D39545',
'#BE8844',
'#A77A44',
'#926D43',
'#7C5F42',
'#665242',
'#4C4141',
'#453D41',
];
var darkCol_3_5 = [
'#F6637B',
'#CC576F',
'#A24C63',
'#784058',
'#633B52',
];
var darkCol_3_10 = [
'#F6637B',
'#E15D75',
'#CC576F',
'#B75269',
'#A24C63',
'#8D465E',
'#784058',
'#633B52',
'#4E354C',
'#433249',
];
var darkCol_4_5 = [
'#944FE9',
'#7E47C7',
'#6740A5',
'#513884',
'#463473',
];
var darkCol_4_10 = [
'#944FE9',
'#894BD8',
'#7E47C7',
'#7344B7',
'#6740A5',
'#5C3C95',
'#513884',
'#463473',
'#3A3162',
'#352F59',
];
var darkCol_5_5 = [
'#4385E3',
'#3D73C2',
'#3760A2',
'#304E81',
'#2D4571',
];
var darkCol_5_10 = [
'#4385E3',
'#407CD3',
'#3D73C2',
'#3A6AB2',
'#3760A2',
'#345792',
'#304E81',
'#2D4571',
'#2A3B61',
'#293758',
];
var darkCol_dashGauge = [
[0.125, '#009245'],
[0.25, '#39b54a'],
[0.375, '#d9e021'],
[0.5, '#fcee21'],
[0.625, '#fbb03b'],
[0.75, '#f7931e'],
[0.875, '#f15a24'],
[1.0, '#ed1c24'],
];
var darkCol_dashUseStatus = ['#2fad35', '#fb8200', '#fb5a8b'];
var darkCol_dashTodayUsageCost = ['#01ae6a', '#ffb046', '#f6637b'];
var darkCol_dashEnrgUsage = ['#01ae6a', '#4385e3'];
var darkCol_dashReadplcStatus = ['#01ae6a', '#ffb046', '#f6637b'];
var lightCol_dashReadplcStatus = ['#3cc380', '#ffb13b', '#f98694'];
var light_Col = [
'#3CC380',
'#FFB13B',
'#F98694',
'#CF74E5',
'#6A9BF4',
'#29BCA2',
'#EC8D3B',
'#FC749D',
'#CF74E5',
'#7E84FF',
'#83BE01',
'#D58B03',
'#FF7E71',
'#BE6DF0',
'#3FAED2',
'#5DBF63',
'#D1886C',
'#ED71B7',
'#977EE6',
'#7A8EE2',
];
var lightCol_1_5 = [
'#3CC380',
'#5BCD94',
'#7BD6A9',
'#99E0BD',
'#B1E7CC',
];
var lightCol_1_10 = [
'#3CC380',
'#4BC88A',
'#5BCD94',
'#6BD19E',
'#7BD6A9',
'#8ADBB3',
'#99E0BD',
'#A9E5C7',
'#B9E9D1',
'#C9EEDC',
];
var lightCol_2_5 = [
'#FFB13B',
'#FFBE5B',
'#FFCA7A',
'#FFD699',
'#FFE0B1',
];
var lightCol_2_10 = [
'#FFB13B',
'#FFB74A',
'#FFBE5B',
'#FFC46A',
'#FFCA7A',
'#FFD089',
'#FFD699',
'#FFDDA9',
'#FFE3B8',
'#FFE9C8',
];
var lightCol_3_5 = [
'#F98694',
'#FA99A5',
'#FBADB6',
'#FCC0C7',
'#FDCFD4',
];
var lightCol_3_10 = [
'#F98694',
'#F98F9C',
'#FA99A5',
'#FAA3AE',
'#FBADB6',
'#FBB6BF',
'#FCC0C7',
'#FCCAD0',
'#FDD3D8',
'#FDDDE1',
];
var lightCol_4_5 = [
'#CF74E5',
'#D78AE9',
'#DEA1ED',
'#E6B7F1',
'#ECC7F5',
];
var lightCol_4_10 = [
'#CF74E5',
'#D37FE7',
'#D78AE9',
'#DA95EB',
'#DEA1ED',
'#E2ACEF',
'#E6B7F1',
'#EAC2F4',
'#EECDF6',
'#F2D8F8',
];
var lightCol_5_5 = [
'#6A9BF4',
'#82ABF6',
'#9ABBF8',
'#B1CBF9',
'#C3D7FB',
];
var lightCol_5_10 = [
'#6A9BF4',
'#76A3F5',
'#82ABF6',
'#8EB3F7',
'#9ABBF8',
'#A6C3F8',
'#B1CBF9',
'#BED3FA',
'#C9DBFB',
'#D6E3FC',
];
var lightCol_dashGauge = [
[0.125, '#58c06f'],
[0.25, '#7cd574'],
[0.375, '#fbe462'],
[0.5, '#ffd771'],
[0.625, '#ffad7f'],
[0.75, '#ff966e'],
[0.875, '#ff706e'],
[1.0, '#ff6689'],
];
var lightCol_dashUseStatus = ['#00c875', '#fdab3d', '#ff7b8b'];
var lightCol_dashTodayUsageCost = ['#3cc380', '#cf74e5', '#ffb13b'];
var lightCol_dashEnrgUsage = ['#ce83e0', '#78a3f3'];
var lightCol_dashReadplcStatus = ['#3cc380', '#ffb13b', '#f98694'];
var tmpChrtOp;
if (this.widgetId || this.widgetData) {
tmpChrtOp =
state.pageData[this.parentPrgmId][this.widgetId][this.widgetData][
this.chartName
];
} else if(this.modalId || this.modalDataKey) {
tmpChrtOp =
state.pageData[this.parentPrgmId][this.modalId][this.modalDataKey][this.chartName];
} else {
tmpChrtOp = state.pageData[this.parentPrgmId][this.chartName];
}
if (this.chartColor != undefined) {
if (this.isDarkMode) {
tmpChrtOp.color = eval('darkCol_' + this.chartColor);
} else {
tmpChrtOp.color = eval('lightCol_' + this.chartColor);
}
} else {
if (this.isDarkMode) {
tmpChrtOp.color = dark_Col;
} else {
tmpChrtOp.color = light_Col;
}
}
return tmpChrtOp;
},
}),
},
methods: {
onClick(event, instance, ECharts) {
console.log('onClick : ', event);
this.$emit('click', event);
},
onDblClick(event, instance, ECharts) {
console.log('onDblClick : ', event);
this.$emit('dblclick', event);
},
onRightClick(event, instance, ECharts) {
console.log('onRightClick : ', event);
this.$emit('rclick', event);
},
onLegendSelect(params) {
const myChart = this.$refs.VChart;
for (const key of params) {
//차트 instance에 'legendSelect' action 전달
myChart.dispatchAction({
type: 'legendSelect',
name: key,
});
}
},
onLegendUnSelect(params) {
const myChart = this.$refs.VChart;
for (const key of params) {
//차트 instance에 'legendUnSelect' action 전달
myChart.dispatchAction({
type: 'legendUnSelect',
name: key,
});
}
},
onLegendSelectChanged(params) {
const obj = params.selected;
this.legendSeletedList = obj;
},
onGetChangedLegendSeletedList() {
return this.legendSeletedList;
},
onGetLegendSelectedList() {
const myChart = this.$refs.VChart;
return myChart.getOption().legend[0].selected;
},
clear() {
const myChart = this.$refs.VChart;
myChart.clear();
},
},
};
</script>

View File

@ -0,0 +1,103 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small :color="required ? '#fb8200' : 'primary'" class="mr-1"
>mdi-record-circle</v-icon
>
{{ label }}
</label>
</v-col>
<v-col :cols="label ? textCols : ''">
<v-checkbox
v-model="chkValue"
:disabled="disabledFlag"
:readonly="readonly || false"
:required="required || false"
:false-value="false"
:color="isDarkMode ? '#fff' : '#4777d9'"
@change="modifyValue"
></v-checkbox>
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
label: {
type: String,
require: true,
},
isDarkMode: {
type: Boolean,
require: false,
default: false,
},
required: {
type: Boolean,
require: false,
default: false,
},
readonly: {
type: Boolean,
require: false,
default: false,
},
valueNm: {
type: String,
require: true,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 7,
},
},
data() {
return {
chkValue: false,
testData: false,
disabledFlag: false,
};
},
computed: {
...mapState({
searchParam: state => state.pageData,
myBindingDara(state) {
return state.pageData[this.parentPrgmId][this.valueNm];
},
}),
},
watch: {
myBindingDara: {
deep: true,
handler(val) {
this.chkValue = val;
},
},
},
created() {
this.chkValue = this.searchParam[this.parentPrgmId][this.valueNm];
},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
modifyValue(e) {
return this.setPageData({ [this.valueNm]: e });
},
},
};
</script>
<style></style>

View File

@ -0,0 +1,145 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="location == 'front'" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon v-if="icon" x-small :color="required ? '#fb8200' : 'primary'" class="mr-1"
>mdi-record-circle</v-icon
>
{{ label }}
</label>
</v-col>
<v-col :cols="textCols" @click="modifyValue">
<v-checkbox
v-model="chkValue"
:disabled="disabledFlag"
:readonly="readonly || false"
:required="required || false"
:false-value="false"
:color="isDarkMode ? '#fff' : '#4777d9'"
@change="modifyValue"
></v-checkbox>
</v-col>
<v-col v-if="location == 'rear'" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon v-if="icon" x-small :color="required ? '#fb8200' : 'primary'" class="mr-1"
>mdi-record-circle</v-icon
>
{{ label }}
</label>
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
label: {
type: String,
require: false,
},
isDarkMode: {
type: Boolean,
require: false,
default: false,
},
required: {
type: Boolean,
require: false,
default: false,
},
readonly: {
type: Boolean,
require: false,
default: false,
},
valueNm: {
type: String,
require: true,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 7,
},
icon: {
type: Boolean,
require: false,
default: true,
},
location: {
type: String,
require: false,
default: 'front'
},
disabledCheckOption: {
type: String,
require: false
}
},
data() {
return {
chkValue: false,
testData: false,
disabledFlag: false,
};
},
computed: {
...mapState({
searchParam: state => state.pageData,
myBindingData(state) {
return state.pageData[this.parentPrgmId][this.valueNm];
},
bindingDisabledCheckOption(state) {
if(state.pageData[this.parentPrgmId][this.disabledCheckOption]!=undefined){
return state.pageData[this.parentPrgmId][this.disabledCheckOption];
}
}
}),
},
watch: {
myBindingData: {
deep: true,
handler(val) {
this.chkValue = val;
},
},
bindingDisabledCheckOption(val) {
this.disabledFlag = val;
}
},
created() {
this.chkValue = this.searchParam[this.parentPrgmId][this.valueNm];
if(this.searchParam[this.parentPrgmId][this.disabledCheckOption]!=undefined){
this.disabledFlag = this.searchParam[this.parentPrgmId][this.disabledCheckOption]
}
},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
modifyValue(e) {
if(this.disabledFlag==true&&e.target != undefined){
alert('기간이 한 시간 이내일 경우만 선택할 수 있습니다.')
}else{
if(e.target == undefined){
return this.setPageData({ [this.valueNm]: e });
}
}
},
},
};
</script>
<style></style>

View File

@ -0,0 +1,98 @@
<template>
<!-- <v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small color="primary" class="mr-1">mdi-record-circle</v-icon>
{{ label }}
</label>
</v-col> -->
<v-radio-group
v-model="selected"
required:rules="radioRules"
row
hide-details
dense
>
<v-radio
v-for="item in radioList"
:key="item.label"
:label="item.label"
:value="item.value"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
</v-radio-group>
<!-- @change="updateBlocCode($event)" -->
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
import Utility from '~/plugins/utility';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 7,
},
radioList: {
type: Array,
require: true,
},
radioValue: {
type: String,
require: false,
default: 'commRadio',
},
},
data() {
return {
labelPrepend: true,
// selected:"CYC_DAY"
};
},
computed: {
...mapState({
isDarkMode: 'isDarkMode',
searchParam: state => state.pageData,
}),
selected: {
get() {
return this.searchParam[this.parentPrgmId][this.radioValue];
},
set(value) {
return this.setPageData({ [this.radioValue]: value });
},
},
},
watch: {
selected(value) {
// 주기에 따른 오늘 기준 기본 날짜 세팅
this.setDefaultDate(value);
},
},
created() {
// this.setDefaultDate(this.searchParam[this.parentPrgmId].cmCycle);
},
async mounted() {},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
...mapActions({}),
setDefaultDate(value) {
this.setPageData({ [this.radioValue]: value });
},
},
};
</script>
<style></style>

View File

@ -0,0 +1,442 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small :color="required ? '#fb8200' : 'primary'" class="mr-1"
>mdi-record-circle</v-icon
>
{{ label }}
</label>
</v-col>
<v-col :cols="label ? textCols : ''">
<div class="datepicker-container">
<v-text-field
id="startpicker"
ref="startpicker"
v-model="fromDtValue"
:class="isRange ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="startpicker-container" id="startpicker-container"></div>
</template>
</v-text-field>
<div v-show="isRange" class="mx-3" :style="{ lineHeight: 0 }">~</div>
<v-text-field
v-show="isRange"
id="endpicker"
ref="endpicker"
v-model="toDtValue"
:class="isRange ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="endpicker-container" id="endpicker-container"></div>
</template>
</v-text-field>
</div>
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import TuiDatepicker from 'tui-date-picker';
import Utility from '~/plugins/utility';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
label: {
type: String,
require: false,
},
timePicker: {
type: Boolean,
require: false,
default: false,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 8,
},
required: {
type: Boolean,
require: false,
default: false,
},
isRangeOption:{
type:Boolean,
require:false,
default: true
}
},
data() {
return {
today: new Date(),
startDatepickerInstance: null,
endDatepickerInstance: null,
startDtValue: null,
endDtValue: null,
cmCycleFlag: false,
};
},
computed: {
...mapState({
searchParam(state) {
return state.pageData[this.parentPrgmId];
},
}),
myCmCycle() {
return this.searchParam.cmCycle;
},
myOptions() {
let returnObj = {};
switch (this.myCmCycle) {
case 'CYC_YEAR':
returnObj = {
type: 'year',
viewFormat: 'YYYY',
pickerFormat: 'YYYY',
sendFormat: 'YYYY',
};
break;
case 'CYC_MONTH':
returnObj = {
type: 'month',
viewFormat: 'YYYY-MM',
pickerFormat: 'YYYY-MM',
sendFormat: 'YYYYMM',
};
break;
case 'CYC_DAY':
returnObj = {
type: 'date',
viewFormat: 'YYYY-MM-DD',
pickerFormat: 'yyyy-MM-dd',
sendFormat: 'YYYYMMDD',
};
break;
case 'CYC_HOUR':
returnObj = {
type: 'date',
viewFormat: 'YYYY-MM-DD' + (this.timePicker ? ' HH:mm:ss' : ''),
pickerFormat: 'yyyy-MM-dd' + (this.timePicker ? ' HH:mm A' : ''),
sendFormat: this.timePicker ? 'YYYY-MM-DD HH:mm:ss' : 'YYYYMMDD',
};
// returnObj = { type: "day", format: "YYYY-MM-DD HH:mm:ss" };
break;
default:
break;
}
return returnObj;
},
// maxDate() {
// return Utility.setFormatDate("today", this.myOptions.format);
// },
fromDtValue() {
return Utility.setFormatDate(
this.searchParam.fromDt,
this.myOptions.viewFormat,
);
},
toDtValue() {
return Utility.setFormatDate(
this.searchParam.toDt,
this.myOptions.viewFormat,
);
},
toDtChange(){
return {
isCheck:this.searchParam.isCheck ,
toDt : Utility.setFormatDate(
this.searchParam.toDt,
this.myOptions.viewFormat,
)};
},
fromDtChange(){
return {
isCheck:this.searchParam.isCheck ,
fromDt : Utility.setFormatDate(
this.searchParam.fromDt,
this.myOptions.viewFormat,
)};
},
defaultRange() {
return this.searchParam.defaultRange
? this.searchParam.defaultRange[this.myCmCycle]
: null;
},
isRange() {
return (
(this.defaultRange !== null && this.defaultRange > 0 && this.isRangeOption) ||
this.defaultRange === 'no limite'
);
},
},
watch: {
myCmCycle() {
this.cmCycleFlag = false;
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
this.startDatepickerInstance.setType(this.myOptions.type);
this.endDatepickerInstance.setType(this.myOptions.type);
},
fromDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.toDtValueChkRang(newVal);
this.startDatepickerInstance.setDate(new Date(newVal));
} else {
this.setPageData({ isFind: true });
}
},
toDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.fromDtValueChkRang(newVal);
this.endDatepickerInstance.setDate(new Date(newVal));
}
},
fromDtChange:{
deep:true,
handler(){
if(this.fromDtChange.isCheck){
this.fromDtChanged(this.fromDtChange.fromDt);
this.setPageData({
isCheck : false
})
}
}
},
toDtChange:{
deep:true,
handler(){
if(this.toDtChange.isCheck){
this.toDtChanged(this.toDtChange.toDt);
this.setPageData({
isCheck : false
})
}
}
}
},
created() {
if (this.timePicker) {
this.setPageData({
fromDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 00:00:00',
toDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 23:59:59',
});
}
},
mounted() {
const startContainer = document.getElementById('startpicker-container');
const startTarget = document.getElementById('startpicker');
const endContainer = document.getElementById('endpicker-container');
const endTarget = document.getElementById('endpicker');
// datepicker 생성
this.startDatepickerInstance = new TuiDatepicker(startContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: startTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성
this.endDatepickerInstance = new TuiDatepicker(endContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: endTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성 끝
// datepicker 초기값 생성
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
// datepicker 초기값 생성 끝
// datepicker 변경시 이벤트 추가
this.startDatepickerInstance.on('change', () => this.getStartDt());
this.endDatepickerInstance.on('change', () => this.getEndDt());
// datepicker 이벤트는 mount 될때 추가 해주어야 한다.
},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
getStartDt() {
const dt = this.startDatepickerInstance.getDate();
this.setPageData({
fromDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
getEndDt() {
const dt = this.endDatepickerInstance.getDate();
this.setPageData({
toDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
toDtChanged(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.fromDtValueChkRang(newVal);
this.endDatepickerInstance.setDate(new Date(newVal));
}
},
fromDtChanged(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.toDtValueChkRang(newVal);
this.startDatepickerInstance.setDate(new Date(newVal));
} else {
this.setPageData({ isFind: true });
}
},
fromDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.fromDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = compareDt.diff(defaultDt, rangeKey);
if (
(myRange > rangeGap && compareDt.isAfter(defaultDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
// if(this.cmCycleFlag){
this.setPageData({ isFind: true });
// }
// this.cmCycleFlag = true;
} else {
this.setPageData({
fromDt: Utility.setBeforetDate(
this.searchParam,
compareDt,
this.myOptions.sendFormat,
),
});
}
},
toDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.toDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = defaultDt.diff(compareDt, rangeKey);
if (
(myRange > rangeGap && defaultDt.isAfter(compareDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
this.setPageData({ isFind: true });
} else {
this.setPageData({
toDt: Utility.setAftertDate(
this.searchParam,
compareDt,
this.myOptions.sendFormat,
),
});
}
},
},
};
</script>
<style lang="scss" scoped>
.datepicker-container {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
.v-input {
.v-input__append-outer {
margin-top: 0;
margin-left: 0;
#startpicker-container,
#endpicker-container {
width: 100%;
position: absolute;
top: 36px;
left: 0;
}
}
}
}
.v-input__custom {
flex: 0 0 auto;
&.half {
width: calc(50% - 20px);
}
}
::v-deep {
.tui-timepicker-row {
display: flex;
justify-content: space-around;
background-color: #edf4fc;
.tui-timepicker-column.tui-timepicker-colon {
color: #000 !important;
}
}
}
</style>

View File

@ -0,0 +1,525 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small :color="required ? '#fb8200' : 'primary'" class="mr-1"
>mdi-record-circle</v-icon
>
{{ label }}
</label>
</v-col>
<v-col :cols="label ? textCols : ''">
<div class="datepicker-container">
<v-text-field
id="startpicker"
ref="startpicker"
v-model="fromDtValue"
:class="(isRange && !selectBoxTimeItemList.selectTimeValue1) ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="startpicker-container" id="startpicker-container"></div>
</template>
</v-text-field>
<component
v-if="selectBoxTimeItemList.selectTimeValue1"
:parentPrgmId="parentPrgmId"
:is="'SelectBoxTime'"
ref="SelectBox1"
:propsValue="selectTimeValue1"
:itemList="selectTimeValueList1"
:minInterval="selectBoxTimeItemList.minInterval ? selectBoxTimeItemList.minInterval : 1"
@update:propsValue="selectTimeValue1 = $event"
/>
<div v-show="isRange" class="mx-3" :style="{ lineHeight: 0 }">~</div>
<v-text-field
v-show="isRange"
id="endpicker"
ref="endpicker"
v-model="toDtValue"
:class="(isRange && !selectBoxTimeItemList.selectTimeValue2) ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="endpicker-container" id="endpicker-container"></div>
</template>
</v-text-field>
<component
v-if="selectBoxTimeItemList.selectTimeValue2"
:parentPrgmId="parentPrgmId"
:is="'SelectBoxTime'"
ref="SelectBox2"
:propsValue="selectTimeValue2"
:itemList="selectTimeValueList2"
:minInterval="selectBoxTimeItemList.minInterval ? selectBoxTimeItemList.minInterval : 1"
@update:propsValue="selectTimeValue2 = $event"
/>
</div>
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import TuiDatepicker from 'tui-date-picker';
import Utility from '~/plugins/utility';
import SelectBoxTime from '@/components/common/select/SelectBoxTime';
import DateUtility from '~/plugins/dateUtility'
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
label: {
type: String,
require: false,
},
timePicker: {
type: Boolean,
require: false,
default: false,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 8,
},
required: {
type: Boolean,
require: false,
default: false,
},
isRangeOption:{
type:Boolean,
require:false,
default: true
},
selectBoxTimeItemList: {
type: Object,
require: false,
default: () => {
return {};
}
},
selectFromDtUntilTodayFg: {
type:Boolean,
require:false,
default: false
},
selectToDtUntilTodayFg: {
type:Boolean,
require:false,
default: false
},
},
components: {
SelectBoxTime
},
data() {
return {
today: new Date(),
startDatepickerInstance: null,
endDatepickerInstance: null,
startDtValue: null,
endDtValue: null,
fromDtOldVal: null,
toDtOldVal: null,
cmCycleFlag: false,
selectTimeValue1: this.selectBoxTimeItemList.selectTimeValue1 != undefined ? this.selectBoxTimeItemList.selectTimeValue1 : null, // selectBoxTime에 필요한 prop
selectTimeValueList1: this.selectBoxTimeItemList.selectTimeValueList1 != undefined ? this.selectBoxTimeItemList.selectTimeValueList1 : [], // selectBoxTime에 필요한 prop
selectTimeValue2: this.selectBoxTimeItemList.selectTimeValue2 != undefined ? this.selectBoxTimeItemList.selectTimeValue2 : null, // selectBoxTime에 필요한 prop
selectTimeValueList2: this.selectBoxTimeItemList.selectTimeValueList2 != undefined ? this.selectBoxTimeItemList.selectTimeValueList2 : [], // selectBoxTime에 필요한 prop
};
},
computed: {
...mapState({
searchParam(state) {
return state.pageData[this.parentPrgmId];
},
}),
myCmCycle() {
return this.searchParam.cmCycle;
},
myOptions() {
let returnObj = {};
switch (this.myCmCycle) {
case 'CYC_YEAR':
returnObj = {
type: 'year',
viewFormat: 'YYYY',
pickerFormat: 'YYYY',
sendFormat: 'YYYY',
};
break;
case 'CYC_MONTH':
returnObj = {
type: 'month',
viewFormat: 'YYYY-MM',
pickerFormat: 'YYYY-MM',
sendFormat: 'YYYYMM',
};
break;
case 'CYC_DAY':
returnObj = {
type: 'date',
viewFormat: 'YYYY-MM-DD',
pickerFormat: 'yyyy-MM-dd',
sendFormat: 'YYYYMMDD',
};
break;
case 'CYC_HOUR':
returnObj = {
type: 'date',
viewFormat: 'YYYY-MM-DD' + (this.timePicker ? ' HH:mm:ss' : ''),
pickerFormat: 'yyyy-MM-dd' + (this.timePicker ? ' HH:mm A' : ''),
sendFormat: this.timePicker ? 'YYYY-MM-DD HH:mm:ss' : 'YYYYMMDD',
};
// returnObj = { type: "day", format: "YYYY-MM-DD HH:mm:ss" };
break;
default:
break;
}
return returnObj;
},
// maxDate() {
// return Utility.setFormatDate("today", this.myOptions.format);
// },
fromDtValue(val) {
let selectVal = Utility.setFormatDate(
this.searchParam.fromDt,
this.myOptions.viewFormat,
);
if(this.selectFromDtUntilTodayFg){
let today = Utility.setFormatDate(new Date(), "YYYY-MM-DD");
let dayDiff = DateUtility.diff(selectVal,today,'days');
if(dayDiff < 0){
alert('오늘 날짜까지 검색이 가능합니다.');
selectVal = today;
if(this.fromDtOldVal == today){
this.toDtValueChkRang(selectVal);
this.startDatepickerInstance.setDate(new Date(selectVal));
}
}
}
if(this.fromDtOldVal == null){
this.fromDtOldVal = selectVal;
}
return selectVal;
// console.log('fromDtValue!!!',);
// return Utility.setFormatDate(
// this.searchParam.fromDt,
// this.myOptions.viewFormat,
// );
},
toDtValue() {
let selectVal = Utility.setFormatDate(
this.searchParam.toDt,
this.myOptions.viewFormat,
);
if(this.selectToDtUntilTodayFg){
let today = Utility.setFormatDate(new Date(), "YYYY-MM-DD");
let dayDiff = DateUtility.diff(selectVal,today,'days');
if(dayDiff < 0){
alert('오늘 날짜까지 검색이 가능합니다.');
selectVal = today;
if(this.toDtOldVal == today){
this.fromDtValueChkRang(selectVal);
this.endDatepickerInstance.setDate(new Date(selectVal));
}
}
}
if(this.toDtOldVal == null){
this.toDtOldVal = selectVal;
}
return selectVal;
// return Utility.setFormatDate(
// this.searchParam.toDt,
// this.myOptions.viewFormat,
// );
},
defaultRange() {
return this.searchParam.defaultRange
? this.searchParam.defaultRange[this.myCmCycle]
: null;
},
isRange() {
return (
(this.defaultRange !== null && this.defaultRange > 0 && this.isRangeOption) ||
this.defaultRange === 'no limite'
);
},
},
watch: {
selectTimeValue1(val){
if(this.selectBoxTimeItemList.selectTimeValue2 != undefined){
this.setSelectTimeValue2(val);
}else{
this.setPageData({ isFind: true });
}
},
selectTimeValue2(val){
this.setSelectTimeValue1(val);
// this.setPageData({ isFind: true });
},
myCmCycle() {
this.cmCycleFlag = false;
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
this.startDatepickerInstance.setType(this.myOptions.type);
this.endDatepickerInstance.setType(this.myOptions.type);
},
fromDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.toDtValueChkRang(newVal);
this.startDatepickerInstance.setDate(new Date(newVal));
this.fromDtOldVal = newVal;
this.setSelectTimeValue1(this.selectTimeValue2, 'check');
this.setSelectTimeValue2(this.selectTimeValue1, 'check');
} else {
this.setPageData({ isFind: true });
}
},
toDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.fromDtValueChkRang(newVal);
this.endDatepickerInstance.setDate(new Date(newVal));
this.toDtOldVal = newVal;
this.setSelectTimeValue1(this.selectTimeValue2, 'check');
this.setSelectTimeValue2(this.selectTimeValue1, 'check');
}
},
},
created() {
if (this.timePicker) {
this.setPageData({
fromDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 00:00:00',
toDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 23:59:59',
});
}
},
mounted() {
const startContainer = document.getElementById('startpicker-container');
const startTarget = document.getElementById('startpicker');
const endContainer = document.getElementById('endpicker-container');
const endTarget = document.getElementById('endpicker');
// datepicker 생성
this.startDatepickerInstance = new TuiDatepicker(startContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: startTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성
this.endDatepickerInstance = new TuiDatepicker(endContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: endTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성 끝
// datepicker 초기값 생성
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
// datepicker 초기값 생성 끝
// datepicker 변경시 이벤트 추가
this.startDatepickerInstance.on('change', () => this.getStartDt());
this.endDatepickerInstance.on('change', () => this.getEndDt());
// datepicker 이벤트는 mount 될때 추가 해주어야 한다.
},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
getStartDt() {
const dt = this.startDatepickerInstance.getDate();
this.setPageData({
fromDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
getEndDt() {
const dt = this.endDatepickerInstance.getDate();
this.setPageData({
toDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
fromDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.fromDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = compareDt.diff(defaultDt, rangeKey);
if (
(myRange > rangeGap && compareDt.isAfter(defaultDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
// if(this.cmCycleFlag){
this.setPageData({ isFind: true });
// }
// this.cmCycleFlag = true;
} else {
this.setPageData({
fromDt: Utility.setBeforetDate(
this.searchParam,
compareDt,
this.myOptions.sendFormat,
),
});
}
},
toDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.toDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = defaultDt.diff(compareDt, rangeKey);
if (
(myRange > rangeGap && defaultDt.isAfter(compareDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
this.setPageData({ isFind: true });
} else {
this.setPageData({
toDt: Utility.setAftertDate(
this.searchParam,
compareDt,
this.myOptions.sendFormat,
),
});
}
},
setSelectTimeValue2(val, type='default'){
this.setPageData({selectTimeValue1:val});
let toDt = this.toDtOldVal;
let fromDt = this.fromDtOldVal;
let dayDiff = DateUtility.diff(fromDt,toDt,'days');
let selectTimeValueList2 = this.selectTimeValueList2.map(item => {
return item.value;
});
if(dayDiff <= 0 && selectTimeValueList2.indexOf(this.selectTimeValue2) < selectTimeValueList2.indexOf(val)){
// this.selectTimeValue2 = selectTimeValueList2[selectTimeValueList2.indexOf(val)];
this.selectTimeValue2 = val;
this.setPageData({selectTimeValue2:val});
}else{
if(type=='default'){
this.setPageData({ isFind: true });
}
}
},
setSelectTimeValue1(val, type='default'){
this.setPageData({selectTimeValue2:val});
let toDt = this.toDtOldVal;
let fromDt = this.fromDtOldVal;
let dayDiff = DateUtility.diff(fromDt,toDt,'days');
let selectTimeValueList2 = this.selectTimeValueList2.map(item => {
return item.value;
});
if(dayDiff <= 0 && selectTimeValueList2.indexOf(val) < selectTimeValueList2.indexOf(this.selectTimeValue1)){
// this.selectTimeValue1 = selectTimeValueList2[selectTimeValueList2.indexOf(val)];
this.selectTimeValue1 = val;
// this.selectTimeValue1 = this.selectTimeValueList2[selectTimeValueList2.indexOf(val) + 1]
this.setPageData({selectTimeValue1:val});
}else{
if(type=='default'){
this.setPageData({ isFind: true });
}
}
}
},
};
</script>
<style lang="scss" scoped>
.datepicker-container {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
.v-input {
.v-input__append-outer {
margin-top: 0;
margin-left: 0;
#startpicker-container,
#endpicker-container {
width: 100%;
position: absolute;
top: 36px;
left: 0;
}
}
}
}
.v-input__custom {
flex: 0 0 auto;
&.half {
width: calc(50% - 20px);
}
}
::v-deep {
.tui-timepicker-row {
display: flex;
justify-content: space-around;
background-color: #edf4fc;
.tui-timepicker-column.tui-timepicker-colon {
color: #000 !important;
}
}
}
</style>

View File

@ -0,0 +1,442 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small :color="required ? '#fb8200' : 'primary'" class="mr-1"
>mdi-record-circle</v-icon
>
{{ label }}
</label>
</v-col>
<v-col :cols="label ? textCols : ''">
<div class="datepicker-container">
<v-text-field
id="startpicker"
ref="startpicker"
v-model="fromDtValue"
:class="isRange ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="startpicker-container" id="startpicker-container"></div>
</template>
</v-text-field>
<div v-show="isRange" class="mx-3" :style="{ lineHeight: 0 }">~</div>
<v-text-field
v-show="isRange"
id="endpicker"
ref="endpicker"
v-model="toDtValue"
:class="isRange ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="endpicker-container" id="endpicker-container"></div>
</template>
</v-text-field>
</div>
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import TuiDatepicker from 'tui-date-picker';
import Utility from '~/plugins/utility';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
label: {
type: String,
require: false,
},
timePicker: {
type: Boolean,
require: false,
default: false,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 8,
},
required: {
type: Boolean,
require: false,
default: false,
},
isRangeOption:{
type:Boolean,
require:false,
default: true
}
},
data() {
return {
today: new Date(),
startDatepickerInstance: null,
endDatepickerInstance: null,
startDtValue: null,
endDtValue: null,
cmCycleFlag: false,
};
},
computed: {
...mapState({
searchParam(state) {
return state.pageData[this.parentPrgmId];
},
}),
myCmCycle() {
return this.searchParam.cmCycle;
},
myOptions() {
let returnObj = {};
switch (this.myCmCycle) {
case 'CYC_YEAR':
returnObj = {
type: 'year',
viewFormat: 'YYYY',
pickerFormat: 'YYYY',
sendFormat: 'YYYY',
};
break;
case 'CYC_MONTH':
returnObj = {
type: 'month',
viewFormat: 'YYYY-MM',
pickerFormat: 'YYYY-MM',
sendFormat: 'YYYYMM',
};
break;
case 'CYC_DAY':
returnObj = {
type: 'date',
viewFormat: 'YYYY-MM-DD',
pickerFormat: 'yyyy-MM-dd',
sendFormat: 'YYYYMMDD',
};
break;
case 'CYC_HOUR':
returnObj = {
type: 'date',
viewFormat: 'YYYY-MM-DD' + (this.timePicker ? ' HH:mm:ss' : ''),
pickerFormat: 'yyyy-MM-dd' + (this.timePicker ? ' HH:mm A' : ''),
sendFormat: this.timePicker ? 'YYYY-MM-DD HH:mm:ss' : 'YYYYMMDD',
};
// returnObj = { type: "day", format: "YYYY-MM-DD HH:mm:ss" };
break;
default:
break;
}
return returnObj;
},
// maxDate() {
// return Utility.setFormatDate("today", this.myOptions.format);
// },
fromDtValue() {
return Utility.setFormatDate(
this.searchParam.fromDt,
this.myOptions.viewFormat,
);
},
toDtValue() {
return Utility.setFormatDate(
this.searchParam.toDt,
this.myOptions.viewFormat,
);
},
toDtChange(){
return {
isCheck:this.searchParam.isCheck ,
toDt : Utility.setFormatDate(
this.searchParam.toDt,
this.myOptions.viewFormat,
)};
},
fromDtChange(){
return {
isCheck:this.searchParam.isCheck ,
fromDt : Utility.setFormatDate(
this.searchParam.fromDt,
this.myOptions.viewFormat,
)};
},
defaultRange() {
return this.searchParam.defaultRange
? this.searchParam.defaultRange[this.myCmCycle]
: null;
},
isRange() {
return (
(this.defaultRange !== null && this.defaultRange > 0 && this.isRangeOption) ||
this.defaultRange === 'no limite'
);
},
},
watch: {
myCmCycle() {
this.cmCycleFlag = false;
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
this.startDatepickerInstance.setType(this.myOptions.type);
this.endDatepickerInstance.setType(this.myOptions.type);
},
fromDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.toDtValueChkRang(newVal);
this.startDatepickerInstance.setDate(new Date(newVal));
} else {
this.setPageData({ isFind: true });
}
},
toDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.fromDtValueChkRang(newVal);
this.endDatepickerInstance.setDate(new Date(newVal));
}
},
fromDtChange:{
deep:true,
handler(){
if(this.fromDtChange.isCheck){
this.fromDtChanged(this.fromDtChange.fromDt);
this.setPageData({
isCheck : false
})
}
}
},
toDtChange:{
deep:true,
handler(){
if(this.toDtChange.isCheck){
this.toDtChanged(this.toDtChange.toDt);
this.setPageData({
isCheck : false
})
}
}
}
},
created() {
if (this.timePicker) {
this.setPageData({
fromDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 00:00:00',
toDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 23:59:59',
});
}
},
mounted() {
const startContainer = document.getElementById('startpicker-container');
const startTarget = document.getElementById('startpicker');
const endContainer = document.getElementById('endpicker-container');
const endTarget = document.getElementById('endpicker');
// datepicker 생성
this.startDatepickerInstance = new TuiDatepicker(startContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: startTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성
this.endDatepickerInstance = new TuiDatepicker(endContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: endTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성 끝
// datepicker 초기값 생성
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
// datepicker 초기값 생성 끝
// datepicker 변경시 이벤트 추가
this.startDatepickerInstance.on('change', () => this.getStartDt());
this.endDatepickerInstance.on('change', () => this.getEndDt());
// datepicker 이벤트는 mount 될때 추가 해주어야 한다.
},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
getStartDt() {
const dt = this.startDatepickerInstance.getDate();
this.setPageData({
fromDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
getEndDt() {
const dt = this.endDatepickerInstance.getDate();
this.setPageData({
toDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
toDtChanged(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.fromDtValueChkRang(newVal);
this.endDatepickerInstance.setDate(new Date(newVal));
}
},
fromDtChanged(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.toDtValueChkRang(newVal);
this.startDatepickerInstance.setDate(new Date(newVal));
} else {
this.setPageData({ isFind: true });
}
},
fromDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.fromDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = compareDt.diff(defaultDt, rangeKey);
if (
(myRange > rangeGap && compareDt.isAfter(defaultDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
// if(this.cmCycleFlag){
this.setPageData({ isFind: true });
// }
// this.cmCycleFlag = true;
} else {
this.setPageData({
fromDt: Utility.setBeforetDate(
this.searchParam,
compareDt,
this.myOptions.sendFormat,
),
});
}
},
toDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.toDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = defaultDt.diff(compareDt, rangeKey);
if (
(myRange > rangeGap && defaultDt.isAfter(compareDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
this.setPageData({ isFind: true });
} else {
this.setPageData({
toDt: Utility.setAftertDate(
this.searchParam,
compareDt,
this.myOptions.sendFormat,
),
});
}
},
},
};
</script>
<style lang="scss" scoped>
.datepicker-container {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
.v-input {
.v-input__append-outer {
margin-top: 0;
margin-left: 0;
#startpicker-container,
#endpicker-container {
width: 100%;
position: absolute;
top: -260px;
left: 0;
}
}
}
}
.v-input__custom {
flex: 0 0 auto;
&.half {
width: calc(50% - 20px);
}
}
::v-deep {
.tui-timepicker-row {
display: flex;
justify-content: space-around;
background-color: #edf4fc;
.tui-timepicker-column.tui-timepicker-colon {
color: #000 !important;
}
}
}
</style>

View File

@ -0,0 +1,396 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small color="primary" class="mr-1">mdi-record-circle</v-icon>
{{ label }}
</label>
</v-col>
<v-col :cols="label ? textCols : ''">
<div class="datepicker-container">
<v-text-field
id="startpicker"
ref="startpicker"
v-model="fromDtValue"
:class="isRange ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="startpicker-container" id="startpicker-container"></div>
</template>
</v-text-field>
<div v-show="isRange" class="mx-3" :style="{ lineHeight: 0 }">~</div>
<v-text-field
v-show="isRange"
id="endpicker"
ref="endpicker"
v-model="toDtValue"
:class="isRange ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="endpicker-container" id="endpicker-container"></div>
</template>
</v-text-field>
</div>
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import TuiDatepicker from 'tui-date-picker';
import Utility from '~/plugins/utility';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
label: {
type: String,
require: false,
},
timePicker: {
type: Boolean,
require: false,
default: false,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 8,
},
widgetPage: {
type: String,
require: false,
},
},
data() {
return {
today: new Date(),
startDatepickerInstance: null,
endDatepickerInstance: null,
startDtValue: null,
endDtValue: null,
cmCycleFlag: false,
};
},
computed: {
...mapState({
searchParam(state) {
return state.pageData[this.parentPrgmId][this.widgetPage][
this.widgetPage + 'Data'
];
},
}),
myCmCycle() {
return this.$store.state.pageData[this.parentPrgmId][this.widgetPage][
this.widgetPage + 'Data'
].cmCycle;
},
myOptions() {
let returnObj = {};
switch (this.myCmCycle) {
case 'CYC_YEAR':
returnObj = {
type: 'year',
viewFormat: 'YYYY',
pickerFormat: 'YYYY',
sendFormat: 'YYYY',
};
break;
case 'CYC_MONTH':
returnObj = {
type: 'month',
viewFormat: 'YYYY-MM',
pickerFormat: 'YYYY-MM',
sendFormat: 'YYYYMM',
};
break;
case 'CYC_DAY':
returnObj = {
type: 'date',
viewFormat: 'YYYY-MM-DD',
pickerFormat: 'yyyy-MM-dd',
sendFormat: 'YYYYMMDD',
};
break;
case 'CYC_HOUR':
returnObj = {
type: 'date',
viewFormat: 'YYYY-MM-DD' + (this.timePicker ? ' HH:mm:ss' : ''),
pickerFormat: 'yyyy-MM-dd' + (this.timePicker ? ' HH:mm A' : ''),
sendFormat: this.timePicker ? 'YYYY-MM-DD HH:mm:ss' : 'YYYYMMDD',
};
// returnObj = { type: "day", format: "YYYY-MM-DD HH:mm:ss" };
break;
default:
break;
}
return returnObj;
},
// maxDate() {
// return Utility.setFormatDate("today", this.myOptions.format);
// },
fromDtValue() {
return Utility.setFormatDate(
this.$store.state.pageData[this.parentPrgmId][this.widgetPage][
this.widgetPage + 'Data'
].fromDt,
this.myOptions.viewFormat,
);
},
toDtValue() {
return Utility.setFormatDate(
// this.$store.state.pageData[this.parentPrgmId][this.widgetPage][this.widgetPage+'Data'].toDt,
this.$store.state.pageData[this.parentPrgmId][this.widgetPage][
this.widgetPage + 'Data'
].toDt,
this.myOptions.viewFormat,
);
},
defaultRange() {
return this.$store.state.pageData[this.parentPrgmId][this.widgetPage][
this.widgetPage + 'Data'
].defaultRange
? this.$store.state.pageData[this.parentPrgmId][this.widgetPage][
this.widgetPage + 'Data'
].defaultRange[this.myCmCycle]
: null;
},
isRange() {
return (
(this.defaultRange !== null && this.defaultRange > 0) ||
this.defaultRange === 'no limite'
);
},
},
watch: {
myCmCycle() {
this.cmCycleFlag = false;
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
this.startDatepickerInstance.setType(this.myOptions.type);
this.endDatepickerInstance.setType(this.myOptions.type);
},
fromDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.toDtValueChkRang(newVal);
this.startDatepickerInstance.setDate(new Date(newVal));
} else {
this.setPageData({ isFind: true });
}
},
toDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.fromDtValueChkRang(newVal);
this.endDatepickerInstance.setDate(new Date(newVal));
}
},
},
created() {
if (this.timePicker) {
this.setPageData({
fromDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 00:00:00',
toDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 23:59:59',
});
}
},
mounted() {
const startContainer = document.getElementById('startpicker-container');
const startTarget = document.getElementById('startpicker');
const endContainer = document.getElementById('endpicker-container');
const endTarget = document.getElementById('endpicker');
// datepicker 생성
this.startDatepickerInstance = new TuiDatepicker(startContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: startTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성
this.endDatepickerInstance = new TuiDatepicker(endContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: endTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성 끝
// datepicker 초기값 생성
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
// datepicker 초기값 생성 끝
// datepicker 변경시 이벤트 추가
this.startDatepickerInstance.on('change', () => this.getStartDt());
this.endDatepickerInstance.on('change', () => this.getEndDt());
// datepicker 이벤트는 mount 될때 추가 해주어야 한다.
},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
getStartDt() {
const dt = this.startDatepickerInstance.getDate();
this.setPageData({
fromDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
getEndDt() {
const dt = this.endDatepickerInstance.getDate();
this.setPageData({
toDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
fromDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.fromDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = compareDt.diff(defaultDt, rangeKey);
if (
(myRange > rangeGap && compareDt.isAfter(defaultDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
// if(this.cmCycleFlag){
this.setPageData({ isFind: true });
// }
// this.cmCycleFlag = true;
} else {
// this.setPageData({
// fromDt: Utility.setBeforetDate(
// this.$store.state.pageData[this.parentPrgmId][this.widgetPage][this.widgetPage+'Data'].fromDt,
// compareDt,
// this.myOptions.sendFormat
// )
// });
this.$store.state.pageData[this.parentPrgmId][this.widgetPage][
this.widgetPage + 'Data'
].fromDt = Utility.setBeforetDate(
this.$store.state.pageData[this.parentPrgmId][this.widgetPage][
this.widgetPage + 'Data'
],
compareDt,
this.myOptions.sendFormat,
);
}
},
toDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.toDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = defaultDt.diff(compareDt, rangeKey);
if (
(myRange > rangeGap && defaultDt.isAfter(compareDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
this.setPageData({ isFind: true });
} else {
this.setPageData({
toDt: Utility.setAftertDate(
this.$store.state.pageData[this.parentPrgmId][this.widgetPage][
this.widgetPage + 'Data'
],
compareDt,
this.myOptions.sendFormat,
),
});
}
},
},
};
</script>
<style lang="scss" scoped>
.datepicker-container {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
.v-input {
.v-input__append-outer {
margin-top: 0;
margin-left: 0;
#startpicker-container,
#endpicker-container {
width: 100%;
position: absolute;
top: 36px;
left: 0;
}
}
}
}
.v-input__custom {
flex: 0 0 auto;
&.half {
width: calc(50% - 20px);
}
}
::v-deep {
.tui-timepicker-row {
display: flex;
justify-content: space-around;
background-color: #edf4fc;
.tui-timepicker-column.tui-timepicker-colon {
color: #000 !important;
}
}
}
</style>

711
components/common/Grid.vue Normal file
View File

@ -0,0 +1,711 @@
<template>
<tui-grid
:ref="['tuigrid' + gridName]"
:data="chkGridData"
:columns="chkGridColumns"
:options="chkGridOptions"
@focusChange="focusChangeEvt"
@click="startEditing"
@editingFinish="editingFinish"
@dblclick="dblClick"
@mouseover="mouseoverEvent"
@mouseout="mouseoutEvent"
/>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
gridName: {
type: String,
require: true,
},
dataPath: {
type: Object,
require: false,
default: null,
},
editorGrid: {
type: Boolean,
default: false,
require: false,
},
innerTabGridInfo: {
type: Object,
default: null,
},
selectedRowDataWatchFlag: {
type: Boolean,
require: false,
default: false,
},
preventFocusChangeEventFlag: {
type: Boolean,
require: false,
default: false,
},
// gridInstance Array
preventFocusChangeEventTargetGridList: {
type: Array,
require: false,
default: null,
},
columnClickEventFlag: {
type: Boolean,
require: false,
default: false,
},
mouseoverEvent: {
type: Function,
default() {
return 'Default Function';
},
},
mouseoutEvent: {
type: Function,
default() {
return 'Default Function';
},
},
},
data() {
return {
gridInstance: null,
gridHeight: null,
selecrRowKey: null,
originData: [],
editorStartKey: null,
editorEndKey: null,
scrollBody: null,
gridScrollTop: 0,
gridScrollLeft: 0,
};
},
computed: {
...mapState({
pageData: state => state.pageData,
gridData(state) {
return this.dataPath
? this.dataPath[this.gridName]
: state.pageData[this.parentPrgmId][this.gridName];
},
drawer: state => state.drawer,
activePrgmId(state) {
return state.activeMenuInfo.prgmId;
},
}),
chkGridData() {
// return this.pageData[this.parentPrgmId][this.gridName].data;
return this.gridData.data;
},
chkGridColumns() {
return this.gridData.column;
},
chkGridOptions() {
const options = {
...this.gridData.option,
};
options.treeColumnOptions = {
useIcon: false,
...options.treeColumnOptions,
};
return options;
},
defaultRow() {
return this.gridData.defaultRow;
},
},
watch: {
chkGridData(val) {
this.$refs['tuigrid' + this.gridName].invoke('resetData', val);
},
innerTabGridInfo(val) {
const _this = this;
setTimeout(() => {
_this.refreshLayout();
}, 500);
},
activePrgmId(val) {
const _this = this;
setTimeout(() => {
_this.refreshLayout();
}, 700);
if (val == this.parentPrgmId) {
setTimeout(() => {
if (!_this.innerTabGridInfo) {
//_this.refreshLayout();
_this.scrollBody.scrollTop = _this.gridScrollTop;
_this.scrollBody.scrollLeft = _this.gridScrollLeft;
} else {
if (_this.innerTabGridInfo.tab == _this.innerTabGridInfo.idx) {
//_this.refreshLayout();
_this.scrollBody.scrollTop = _this.gridScrollTop;
_this.scrollBody.scrollLeft = _this.gridScrollLeft;
}
}
}, 1000);
}
},
drawer() {
const _this = this;
setTimeout(() => {
_this.refreshLayout();
}, 500);
},
},
created() {},
async mounted() {
// console.log(this.dataPath);
if (this.gridName) {
this.gridInstance = this.$refs['tuigrid' + this.gridName];
this.scrollBody = document
.getElementsByClassName('tui-grid-rside-area')
[
document.getElementsByClassName('tui-grid-rside-area').length - 1
].getElementsByClassName('tui-grid-body-area')[0];
this.scrollBody.addEventListener('scroll', e => {
this.gridScrollTop = e.target.scrollTop;
this.gridScrollLeft = e.target.scrollLeft;
});
}
},
methods: {
...mapMutations({
setPageData: 'setPageData',
setGridData: 'setGridData',
}),
// true : 현재 행 혹은 연결된 그리드가 수정중인 상태
checkGridState() {
var rowStatList = ['I', 'U', 'D'];
var row = this.gridInstance.invoke('getFocusedCell');
if (row) {
var rowData = this.gridInstance.invoke('getRow', row.rowKey);
if (rowData) {
var rowStat = rowData['rowStat'];
if (rowStatList.includes(rowStat)) {
return true;
}
}
}
if (this.preventFocusChangeEventTargetGridList) {
for (var gridInstance of this.preventFocusChangeEventTargetGridList) {
var dataArr = gridInstance.save();
if (dataArr.length > 0) {
return true;
}
}
}
return false;
},
preventFocusChangeEvent(e) {
var result = false;
if (this.preventFocusChangeEventFlag) {
if (this.checkGridState()) {
e.stop();
return true;
}
}
return result;
},
columnClickEvent(e) {
var result = false;
if (this.columnClickEventFlag) {
return true;
}
return result;
},
getCheckedRowsEvt() {
const checkedRowDataList = this.gridInstance.invoke('getCheckedRows');
return checkedRowDataList;
},
dblClick(nativeEvent) {
if (this.preventFocusChangeEvent(nativeEvent)) {
return;
}
this.$emit(
'dblClick',
this.gridInstance.invoke('dblclick'),
nativeEvent,
this.gridName,
);
},
uncheckEvt(rowData, instance) {
this.gridInstance.invoke('uncheck', rowData.rowKey, instance);
},
checkEvt(rowData, instance) {
this.gridInstance.invoke('check', rowData.rowKey, instance);
},
setSelectionRange(rowKey) {
const rowDatas = this.gridInstance.invoke('getData');
rowDatas.forEach(item => {
if (item.rowKey == rowKey) {
this.gridInstance.invoke(
'addRowClassName',
item.rowKey,
'row-selected',
);
} else {
this.gridInstance.invoke(
'removeRowClassName',
item.rowKey,
'row-selected',
);
}
});
},
focusChangeEvt(e) {
// console.log('focusChangeEvt1...')
if (this.preventFocusChangeEvent(e)) {
// console.log('prevent focusChangeEvt')
return;
}
// console.log('focusChangeEvt2...')
// cell 선택시 row 선택 method
if (e.rowKey >= 0) {
this.$emit(
'getRowsData',
this.gridInstance.invoke('getRow', e.rowKey),
this.gridName,
e.columnName,
);
this.selecrRowKey = e.rowKey;
this.setSelectionRange(e.rowKey);
}
this.sendSelectedRowData(e.rowKey);
},
startEditing(e) {
// console.log('startEditing1...')
if (this.preventFocusChangeEvent(e)) {
// console.log('prevent startEditing')
return;
}
if (this.columnClickEvent(e)) {
this.$emit('columnClick', e, this.gridName);
return;
}
// console.log('startEditing2...')
if (this.editorGrid && e.rowKey >= 0) {
this.editorStartKey = e.rowKey;
// console.log("E::", this.gridInstance.invoke("getRow", e.rowKey));
this.gridInstance.invoke('startEditing', e.rowKey, e.columnName);
this.$emit(
'getRowsData',
this.gridInstance.invoke('getRow', e.rowKey),
this.gridName,
e.columnName,
);
this.setSelectionRange(e.rowKey);
}
},
async editingFinish(e) {
// console.log("Editing END E::", e);
// editor 간 이동시 수정되는 문제 수정
// e.rowEditingFg: grid의 한 row를 한번에 수정할 시 각각의 cell 마다 click 이벤트가 발생하지 않아 this.editorStartKey값이 제대로 입력 되지 않는 경우를 대비하여 만든 Fg
if (this.editorGrid) {
this.editorEndKey = Number.isInteger(e.rowKey) ? e.rowKey : null;
if (e.rowEditingFg == undefined && this.editorStartKey >= 0) {
if (this.editorStartKey != this.editorEndKey) {
this.editorStartKey = null;
this.editorEndKey = null;
return false;
}
}
}
const rowIdxKey = this.editorGrid ? this.editorEndKey : this.gridInstance.invoke('getFocusedCell').rowKey
// editor 간 이동시 수정되는 문제 수정 끝
const columnName = e.columnName;
const value = e.value;
const editingData = {
...e,
rowKey: rowIdxKey,
};
const isBaseRow = this.isBaseDataRow(rowIdxKey);
// console.log("END E::", rowIdxKey, e);
const rowStat = this.gridInstance.invoke('getRow', rowIdxKey).rowStat;
if (rowStat == 'D') {
this.gridInstance.invoke(
'removeRowClassName',
rowIdxKey,
'row-removed',
);
}
await this.gridInstance.invoke(
e.rowEditingFg != undefined ? 'setValue' : this.editorGrid ? 'finishEditing' : 'setValue',
rowIdxKey,
columnName,
value,
);
if (isBaseRow) {
const isSameData = await this.compareData(editingData);
if (isSameData) {
this.gridInstance.invoke(
'removeRowClassName',
rowIdxKey,
'row-modify',
);
this.updateData('clear', rowIdxKey);
} else {
this.gridInstance.invoke('addRowClassName', rowIdxKey, 'row-modify');
this.updateData('modify', rowIdxKey);
}
}
if (
e.ignoreUpdateDataInfoFlag === undefined ||
e.ignoreUpdateDataInfoFlag === false
) {
this.$emit('updateDataInfo', {
rowIdxKey: rowIdxKey,
rowStat: rowStat,
columnName: columnName,
value: value,
rowData: this.gridInstance.invoke('getRow', rowIdxKey),
});
}
this.sendSelectedRowData();
},
async addRow(setData, argRowKey) {
// 그리드가 수정중인 상태면 addRow를 하지 않음
if (this.preventFocusChangeEventFlag) {
if (this.checkGridState()) {
return true;
}
}
const addData = !setData
? this.defaultRow
: Object.assign(this.defaultRow, setData);
// 열 앞에 데이터 추가
// if (argRowKey != undefined && argRowKey != null) {
this.gridInstance.invoke('appendRow', addData, {
focus: true,
});
// } else {
// this.gridInstance.invoke('prependRow', addData, {
// focus: true,
// });
// }
// this.gridInstance.invoke('prependRow', addData, {
// focus: true,
// });
this.$nextTick(() => {
var addRowKey = this.gridInstance.invoke('getFocusedCell').rowKey;
if (argRowKey) {
addRowKey = argRowKey;
}
this.gridInstance.invoke(
'removeRowClassName',
addRowKey,
'row-removed',
);
this.gridInstance.invoke('addRowClassName', addRowKey, 'row-insert');
this.updateData('insert', addRowKey);
});
},
async appendRow() {
this.gridInstance.invoke('appendRow', {
focus: true,
});
},
async addTreeRow(setData) {
// tree append의 경우 무한루프가 발행하는 버그현상으로 appendRows로 추가
const addData = !setData
? this.defaultRow
: Object.assign(this.defaultRow, setData);
this.gridInstance.invoke('appendRows', [addData]);
this.$nextTick(() => {
const rowDatas = this.gridInstance.invoke('getData');
const addRowKey = rowDatas[rowDatas.length - 1].rowKey;
this.gridInstance.invoke(
'removeRowClassName',
addRowKey,
'row-removed',
);
this.gridInstance.invoke('addRowClassName', addRowKey, 'row-insert');
this.updateData('insert', addRowKey);
this.gridInstance.invoke('focus', addRowKey);
});
},
async removeRow(delType, argRowKey) {
var rowIdxKey = this.gridInstance.invoke('getFocusedCell').rowKey;
if (typeof argRowKey == 'number') {
rowIdxKey = argRowKey;
}
const rowStat = this.gridInstance.invoke('getRow', rowIdxKey).rowStat;
if (rowStat === 'D') {
this.gridInstance.invoke(
'removeRowClassName',
rowIdxKey,
'row-removed',
);
this.updateData('clear', rowIdxKey);
} else {
if (!this.isBaseDataRow(rowIdxKey)) {
this.gridInstance.invoke('removeRow', rowIdxKey);
let nextFocus = Number(rowIdxKey) - 1;
if (!this.isBaseDataRow(nextFocus)) nextFocus = 0;
this.gridInstance.invoke('focus', nextFocus);
} else {
this.gridInstance.invoke('addRowClassName', rowIdxKey, 'row-removed');
this.updateData('delete', rowIdxKey);
if (delType == 'immediately')
this.gridInstance.invoke('removeRow', rowIdxKey);
}
}
},
async removeTreeRow(delType) {
const rowIdxKey = this.gridInstance.invoke('getFocusedCell').rowKey;
const rowStat = this.gridInstance.invoke('getRow', rowIdxKey).rowStat;
if (rowStat === 'D') {
this.gridInstance.invoke(
'removeRowClassName',
rowIdxKey,
'row-removed',
);
this.updateData('clear', rowIdxKey);
} else {
if (!this.isBaseDataRow(rowIdxKey)) {
this.updateData('clear', rowIdxKey);
this.gridInstance.invoke('removeRow', rowIdxKey);
let nextFocus = Number(rowIdxKey) - 1;
if (!this.isBaseDataRow(nextFocus)) nextFocus = 0;
this.gridInstance.invoke('focus', nextFocus);
} else {
this.gridInstance.invoke('addRowClassName', rowIdxKey, 'row-removed');
this.updateData('delete', rowIdxKey);
if (delType == 'immediately')
this.gridInstance.invoke('removeRow', rowIdxKey);
}
}
},
isBaseDataRow(rowKey) {
// 기존데이터 여부 확인 (추가된 데이터 X)
const findRow = this.gridInstance.invoke('findRows', { rowKey: rowKey });
return findRow && findRow[0] && findRow[0].rowStat != 'I' ? true : false;
},
compareData(data) {
// rowStat key값 제거
const dataKeyArr = Object.keys(this.defaultRow);
const rowStatIdx = dataKeyArr.indexOf('rowStat');
dataKeyArr.splice(rowStatIdx, 1);
// rowStat key값 제거 끝
const selectedRowData = this.gridInstance.invoke('getRow', data.rowKey);
this.getOriginData();
const rowData = this.originData.find(item => {
return item.rowKey == data.rowKey;
});
let count = 0;
// console.log("dataKeyArr", dataKeyArr);
// console.log("selectedRowData", selectedRowData);
for (let i = 0; i < dataKeyArr.length; i++) {
// console.log(dataKeyArr[i], selectedRowData[dataKeyArr[i]], rowData[dataKeyArr[i]]);
if (selectedRowData[dataKeyArr[i]] == rowData[dataKeyArr[i]]) {
count++;
}
}
return dataKeyArr.length == count ? true : false;
},
getOriginData() {
this.chkGridData.forEach(item => {
this.originData.push(item);
if (item._children) {
this.getChildrenData(item._children);
}
});
},
getChildrenData(children) {
children.forEach(item => {
this.originData.push(item);
if (item._children) {
this.getChildrenData(item._children);
}
});
},
updateData(updateType, rowIdxKey) {
let type = '';
switch (updateType) {
case 'insert':
type = 'I';
break;
case 'modify':
type = 'U';
break;
case 'delete':
type = 'D';
break;
case 'clear':
type = null;
break;
}
this.gridInstance.invoke('setValue', rowIdxKey, 'rowStat', type);
},
// 지정 로우 선택상태
focus(rowInfo) {
this.gridInstance.invoke(
'focus',
rowInfo.rowKey,
rowInfo.columnName,
rowInfo.setScroll,
);
},
// 트리 전체 접기
expandAll() {
this.gridInstance.invoke('expandAll');
},
// 트리 전체 펼치기
collapseAll() {
this.gridInstance.invoke('collapseAll');
},
save() {
const saveTargetRows = this.gridInstance.invoke('getModifiedRows');
// createdRows | deletedRows | updatedRows
const createdRows = saveTargetRows.createdRows;
const deletedRows = saveTargetRows.deletedRows;
const updatedRows = saveTargetRows.updatedRows;
const dataArr = [...createdRows, ...deletedRows, ...updatedRows]
.filter(item => item.rowStat)
.map(item => {
delete item.rowKey;
return item;
});
// console.log("dataArr::", dataArr, saveTargetRows);
return dataArr;
},
getData() {
return this.gridInstance.invoke('getData');
},
getCheckedRows() {
return this.gridInstance.invoke('getCheckedRows');
},
getCheckedRowKeys() {
return this.gridInstance.invoke('getCheckedRowKeys');
},
setCheck(list) {
// console.log("setCheck:: ", list);
list.map(item => this.gridInstance.invoke('check', item));
},
refreshLayout() {
this.gridInstance.invoke('refreshLayout');
},
refreshGrid(){
// console.log("refreshLayout",this.$refs['tuigrid' + this.gridName])
var store = this.$refs['tuigrid' + this.gridName].gridInstance.store;
var containerEl = this.$refs['tuigrid' + this.gridName].$el;
// var containerEl = document.querySelector('.tui-grid-container')
var parentEl = containerEl.parentElement;
// console.log("containerEl : ",containerEl);
// console.log('parentEl : ', parentEl)
// function refreshLayout(store, containerEl, parentEl) {
var dimension = store.dimension;
var autoWidth = dimension.autoWidth, fitToParentHeight = dimension.fitToParentHeight;
var clientHeight = containerEl.clientHeight, clientWidth = containerEl.clientWidth, scrollTop = containerEl.scrollTop, scrollLeft = containerEl.scrollLeft;
var _a = containerEl.getBoundingClientRect(), top = _a.top, left = _a.left;
this.setOffsetTop(store, top + scrollTop);
// store.dimension.setOffsetTop = top + scrollTop;
this.setOffsetLeft(store, left + scrollLeft);
// store.dimension.headerHeight = left + scrollLeft;
this.setWidth(store, clientWidth, autoWidth);
// store.dimension.autoWidth = autoWidth;
// store.dimension.width = clientWidth;
// console.log("###",getComputedStyle(parentEl));
// console.log("fitToParentHeight : ",fitToParentHeight);
// console.log("parentEl : ",parentEl)
// console.log("parentEl.clientHeight" , parentEl.clientHeight)
// console.log("clientHeight : ",clientHeight);
if (parentEl && parentEl.clientHeight !== clientHeight) {
var _b = getComputedStyle(parentEl), paddingTop = _b.paddingTop, paddingBottom = _b.paddingBottom;
this.setHeight(store, parentEl.clientHeight - (parseFloat(paddingTop) + parseFloat(paddingBottom)));
}
// }
},
setOffsetTop(store, offsetTop) {
// console.log("setOffsetTop");
store.dimension.offsetTop = offsetTop;
},
setWidth(_a, width, autoWidth) {
// console.log("setWidth");
var dimension = _a.dimension;
dimension.autoWidth = autoWidth;
dimension.width = width;
},
setHeaderHeight(store, height) {
// console.log("setHeaderHeight")
store.dimension.headerHeight = height;
},
setOffsetLeft(store, offsetLeft) {
// console.log("setOffsetLeft")
store.dimension.offsetLeft = offsetLeft;
},
setHeight(_a,height){
// console.log("setHeight");
var dimension = _a.dimension;
var headerHeight = dimension.headerHeight, summaryHeight = dimension.summaryHeight, tableBorderWidth = dimension.tableBorderWidth;
dimension.bodyHeight = height - headerHeight - summaryHeight - tableBorderWidth;
},
sendSelectedRowData(eventRowKey) {
if (this.selectedRowDataWatchFlag) {
var rowKey =
eventRowKey === undefined
? this.gridInstance.invoke('getFocusedCell').rowKey
: eventRowKey;
var rowData = this.gridInstance.invoke('getRow', rowKey);
this.$emit('sendSelectedRowStatInfo', rowData);
}
},
disableRow(rowKey, withCheckBox = false) {
this.gridInstance.invoke('disableRow', rowKey, withCheckBox);
},
async disabledRow(addRowKey) {
this.$nextTick(() => {
this.gridInstance.invoke(
'removeRowClassName',
addRowKey,
'row-removed',
);
this.gridInstance.invoke('addRowClassName', addRowKey, 'row-disabled');
});
},
// resetData() {
// // console.log("resetData = ", this.tuigridProps.data);
// this.$refs.tuigrid.invoke("resetData", this.tuigridProps.data);
// }
},
};
</script>
<style scoped lang="scss">
::v-deep .tui-grid-container {
.tui-grid-content-area {
.tui-grid-cell-content {
input[type='number'] {
width: 100%;
}
}
}
}
</style>

245
components/common/Grid2.vue Normal file
View File

@ -0,0 +1,245 @@
<template>
<tui-grid
:ref="['tuigrid' + gridName]"
:data="chkGridData"
:columns="chkGridColumns"
:options="chkGridOptions"
@focusChange="focusChangeEvt"
@click="startEditing"
@editingFinish="editingFinish"
/>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
gridName: {
type: String,
require: true,
},
dataPath: {
type: Object,
require: false,
default: null,
},
editorGrid: {
type: Boolean,
default: false,
require: false,
},
},
data() {
return {
gridInstance: null,
gridHeight: null,
selecrRowKey: null,
};
},
computed: {
...mapState({
// pageData: state => state.pageData
pageData(state) {
return this.dataPath
? this.dataPath[this.gridName]
: state.pageData[this.parentPrgmId][this.gridName];
},
}),
chkGridData() {
return this.pageData.data;
},
chkGridColumns() {
return this.pageData.column;
},
chkGridOptions() {
return this.pageData.option;
},
defaultRow() {
return this.pageData.defaultRow;
},
},
watch: {
chkGridData(val) {
this.$refs['tuigrid' + this.gridName].invoke('resetData', val);
},
},
created() {},
async mounted() {
// console.log(this.dataPath);
if (this.gridName) {
this.gridInstance = this.$refs['tuigrid' + this.gridName];
}
},
methods: {
...mapMutations({
setPageData: 'setPageData',
setGridData: 'setGridData',
}),
getCheckedRowsEvt() {
const checkedRowDataList = this.gridInstance.invoke('getCheckedRows');
return checkedRowDataList;
},
uncheckEvt(rowData, instance) {
this.gridInstance.invoke('uncheck', rowData.rowKey, instance);
},
checkEvt(rowData, instance) {
this.gridInstance.invoke('check', rowData.rowKey, instance);
},
focusChangeEvt(e) {
// cell 선택시 row 선택 method
if (!this.editorGrid) {
this.$emit('getRowsData', this.gridInstance.invoke('getRow', e.rowKey));
this.selecrRowKey = e.rowKey;
const rowIndxKey = this.gridInstance.invoke('getIndexOfRow', e.rowKey);
this.gridInstance.invoke('setSelectionRange', {
start: [rowIndxKey, 0],
end: [rowIndxKey, this.gridInstance.columns.length],
});
}
},
startEditing(e) {
if (this.editorGrid) {
this.gridInstance.invoke('startEditing', e.rowKey, e.columnName);
const rowIndxKey = this.gridInstance.invoke('getIndexOfRow', e.rowKey);
this.gridInstance.invoke('setSelectionRange', {
start: [rowIndxKey, 0],
end: [rowIndxKey, this.gridInstance.columns.length],
});
}
},
editingFinish(e) {
// console.log("editingFinish::e", e);
const rowIdxKey = e.rowKey;
const columnName = e.columnName;
const value = e.value;
const isAddRow = this.isBaseDataRow(rowIdxKey);
this.gridInstance.invoke('setValue', rowIdxKey, columnName, value);
if (isAddRow != -1) {
const isSameData = this.compareData(e);
if (!isSameData) {
this.gridInstance.invoke('addRowClassName', rowIdxKey, 'row-modify');
this.updateData('modify', rowIdxKey);
}
}
},
async addRow() {
// 열 앞에 데이터 추가
this.gridInstance.invoke('prependRow', this.defaultRow, {
focus: true,
});
// const aa = this.gridInstance.invoke("getModifiedRows");
// console.log("this.gridInstance", this.gridInstance, aa);
this.$nextTick(() => {
const addRowKey = this.gridInstance.invoke('getFocusedCell').rowKey;
// console.log("addRowKey :: ", addRowKey);
this.gridInstance.invoke('addRowClassName', addRowKey, 'row-insert');
this.updateData('insert', addRowKey);
});
},
removeRow() {
const rowIdxKey = this.gridInstance.invoke('getFocusedCell').rowKey;
this.gridInstance.invoke('addRowClassName', rowIdxKey, 'row-removed');
this.gridInstance.invoke('disableRow', rowIdxKey);
this.updateData('delete', rowIdxKey);
},
externalDataEdit(obj) {
const rowIdxKey = this.gridInstance.invoke('getFocusedCell').rowKey;
const columnName = obj.name;
const value = obj.value;
const isAddRow = this.isBaseDataRow(rowIdxKey);
this.gridInstance.invoke('setValue', rowIdxKey, columnName, value);
if (isAddRow != -1) {
this.gridInstance.invoke('addRowClassName', rowIdxKey, 'row-modify');
this.updateData('modify', rowIdxKey);
}
},
isBaseDataRow(rowKey) {
// 기존데이터 여부 확인 (추가된 데이터 X)
return this.chkGridData.map(item => item.rowKey).indexOf(rowKey);
},
compareData(data) {
const rowData = this.chkGridData.filter(item => {
return item.rowKey == data.rowKey;
})[0];
// console.log(rowData);
return rowData[data.columnName] == data.value;
},
updateData(updateType, rowIdxKey) {
let type = '';
switch (updateType) {
case 'insert':
type = 'I';
break;
case 'modify':
type = 'U';
break;
case 'delete':
type = 'D';
break;
}
this.gridInstance.invoke('setValue', rowIdxKey, 'rowStat', type);
},
// 지정 로우 선택상태
focus(rowInfo) {
this.gridInstance.invoke(
'focus',
rowInfo.rowKey,
rowInfo.columnName,
rowInfo.setScroll,
);
},
// 트리 전체 접기
expandAll() {
this.gridInstance.invoke('expandAll');
},
// 트리 전체 펼치기
collapseAll() {
this.gridInstance.invoke('collapseAll');
},
save() {
const saveTargetRows = this.gridInstance.invoke('getModifiedRows');
// createdRows | deletedRows | updatedRows
const createdRows = saveTargetRows.createdRows.map(item => {
delete item.rowKey;
return item;
});
const deletedRows = saveTargetRows.deletedRows.map(item => {
delete item.rowKey;
return item;
});
const updatedRows = saveTargetRows.updatedRows.map(item => {
delete item.rowKey;
return item;
});
const dataArr = createdRows.concat(deletedRows).concat(updatedRows);
return dataArr;
// this.$emit("saveGrid", saveTargetRows);
},
// resetData() {
// // console.log("resetData = ", this.tuigridProps.data);
// this.$refs.tuigrid.invoke("resetData", this.tuigridProps.data);
// }
},
};
</script>
<style scoped lang="scss">
::v-deep .tui-grid-cell {
&.row-insert {
background-color: #13636c !important;
color: #fff !important;
}
&.row-modify {
background-color: #13636c;
}
&.row-removed {
background-color: red;
}
}
</style>

View File

@ -0,0 +1,354 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small color="primary" class="mr-1">mdi-record-circle</v-icon>
{{ label }}
</label>
</v-col>
<v-col :cols="label ? textCols : ''">
<div class="datepicker-container">
<v-text-field
id="startpicker"
ref="startpicker"
v-model="fromDtValue"
:class="isRange ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="startpicker-container" id="startpicker-container"></div>
</template>
</v-text-field>
<div v-show="isRange" class="mx-3" :style="{ lineHeight: 0 }">~</div>
<v-text-field
v-show="isRange"
id="endpicker"
ref="endpicker"
v-model="toDtValue"
:class="isRange ? 'v-input__custom half' : 'v-input__custom'"
:hide-details="true"
readonly
outlined
>
<template #append>
<v-icon size="20">$icoCalendar</v-icon>
</template>
<template #append-outer>
<div ref="endpicker-container" id="endpicker-container"></div>
</template>
</v-text-field>
</div>
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import TuiDatepicker from 'tui-date-picker';
import Utility from '~/plugins/utility';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
label: {
type: String,
require: false,
},
timePicker: {
type: Boolean,
require: false,
default: false,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 8,
},
},
data() {
return {
today: new Date(),
startDatepickerInstance: null,
endDatepickerInstance: null,
startDtValue: null,
endDtValue: null,
};
},
computed: {
...mapState({
searchParam(state) {
return state.pageData[this.parentPrgmId];
},
}),
myCmCycle() {
return this.searchParam.cmCycle;
},
myOptions() {
let returnObj = {
type: 'date',
viewFormat: 'YYYY-MM-DD',
pickerFormat: 'yyyy-MM-dd',
sendFormat: 'YYYYMMDD',
};
return returnObj;
},
// maxDate() {
// return Utility.setFormatDate("today", this.myOptions.format);
// },
fromDtValue() {
return Utility.setFormatDate(
this.searchParam.fromDt,
this.myOptions.viewFormat,
);
},
toDtValue() {
return Utility.setFormatDate(
this.searchParam.toDt,
this.myOptions.viewFormat,
);
},
defaultRange() {
return this.searchParam.defaultRange
? this.searchParam.defaultRange[this.myCmCycle]
: null;
},
isRange() {
return (
(this.defaultRange !== null && this.defaultRange > 0) ||
this.defaultRange === 'no limite'
);
},
},
watch: {
myCmCycle() {
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
this.startDatepickerInstance.setType(this.myOptions.type);
this.endDatepickerInstance.setType(this.myOptions.type);
},
fromDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.toDtValueChkRang(newVal);
this.startDatepickerInstance.setDate(new Date(newVal));
} else {
this.setPageData({ isFind: true });
}
},
toDtValue(newVal, oldVal) {
if (
this.isRange &&
this.defaultRange !== 'no limite' &&
newVal !== 'Invalid Date' &&
newVal !== oldVal
) {
this.fromDtValueChkRang(newVal);
this.endDatepickerInstance.setDate(new Date(newVal));
}
},
},
created() {
if (this.timePicker) {
this.setPageData({
fromDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 00:00:00',
toDt: Utility.setFormatDate(this.today, 'YYYY-MM-DD') + ' 23:59:59',
});
}
},
mounted() {
const startContainer = document.getElementById('startpicker-container');
const startTarget = document.getElementById('startpicker');
const endContainer = document.getElementById('endpicker-container');
const endTarget = document.getElementById('endpicker');
// datepicker 생성
this.startDatepickerInstance = new TuiDatepicker(startContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: startTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성
this.endDatepickerInstance = new TuiDatepicker(endContainer, {
date: this.today,
language: 'ko',
type: this.myOptions.type, // "date", // type: date || month || year
input: {
element: endTarget,
format: this.myOptions.pickerFormat, //"YYYY-MM-DD" //this.format
},
timePicker: this.timePicker,
calendar: {
showToday: false,
},
});
// datepicker 생성 끝
// datepicker 초기값 생성
this.startDatepickerInstance.setDate(new Date(this.fromDtValue));
// datepicker 초기값 생성 끝
// datepicker 변경시 이벤트 추가
this.startDatepickerInstance.on('change', () => this.getStartDt());
this.endDatepickerInstance.on('change', () => this.getEndDt());
// datepicker 이벤트는 mount 될때 추가 해주어야 한다.
},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
setDatePicker(type, compareDate, formatDate, formatTime) {
let returnDt = null;
const dayjs = require('dayjs');
const compareDt = dayjs(compareDate);
const formatType = formatDate + (formatTime ? ' ' + formatTime : '');
const defaultRange = this.defaultRange;
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
if (type == 'toDt') {
returnDt = compareDt.add(myRange, rangeKey).subtract(1, 'day');
} else {
returnDt = compareDt.subtract(myRange, rangeKey).add(1, 'day');
}
return returnDt.format(formatType);
},
getStartDt() {
const dt = this.startDatepickerInstance.getDate();
this.setPageData({
fromDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
getEndDt() {
const dt = this.endDatepickerInstance.getDate();
this.setPageData({
toDt: Utility.setFormatDate(dt, this.myOptions.sendFormat),
});
},
fromDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.fromDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = compareDt.diff(defaultDt, rangeKey);
if (
(myRange > rangeGap && compareDt.isAfter(defaultDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
// if(this.cmCycleFlag){
this.setPageData({ isFind: true });
// }
// this.cmCycleFlag = true;
} else {
this.setPageData({
fromDt: this.setDatePicker(
'fromDt',
compareDt,
this.myOptions.sendFormat,
),
});
}
},
toDtValueChkRang(newDt) {
const defaultDt = this.$dayjs(this.toDtValue);
const compareDt = this.$dayjs(newDt);
const newDefault = Utility.setNewDefaultRange(
this.myCmCycle,
this.defaultRange,
);
const myRange = newDefault.range;
const rangeKey = newDefault.key;
const rangeGap = defaultDt.diff(compareDt, rangeKey);
if (
(myRange > rangeGap && defaultDt.isAfter(compareDt)) ||
defaultDt.format(this.myOptions.sendFormat) ===
compareDt.format(this.myOptions.sendFormat)
) {
this.setPageData({ isFind: true });
} else {
this.setPageData({
toDt: this.setDatePicker(
'toDt',
compareDt,
this.myOptions.sendFormat,
),
});
}
},
},
};
</script>
<style lang="scss" scoped>
.datepicker-container {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
.v-input {
.v-input__append-outer {
margin-top: 0;
margin-left: 0;
#startpicker-container,
#endpicker-container {
width: 100%;
position: absolute;
top: 36px;
left: 0;
}
}
}
}
.v-input__custom {
flex: 0 0 auto;
&.half {
width: calc(50% - 20px);
}
}
::v-deep {
.tui-timepicker-row {
display: flex;
justify-content: space-around;
background-color: #edf4fc;
.tui-timepicker-column.tui-timepicker-colon {
color: #000 !important;
}
}
}
</style>

View File

@ -0,0 +1,138 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small color="primary" class="mr-1">mdi-record-circle</v-icon>
{{ label }}
</label>
</v-col>
<v-col :cols="label ? 'auto' : ''">
<v-radio-group
v-model="selected"
required:rules="radioRules"
row
dense
:hide-details="true"
>
<v-radio
label="월별"
value="CYC_YEAR"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
<v-radio
label="일별"
value="CYC_MONTH"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
<v-radio
label="시간별"
value="CYC_DAY"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
</v-radio-group>
<!-- @change="updateBlocCode($event)" -->
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
import Utility from '~/plugins/utility';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
label: {
type: String,
require: false,
default: '주기',
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 7,
},
},
data() {
return {
labelPrepend: true,
// selected:"CYC_DAY"
};
},
computed: {
...mapState({
searchParam: state => state.pageData,
isDarkMode: 'isDarkMode',
}),
selected: {
get() {
return this.searchParam[this.parentPrgmId].cmCycle;
},
set(value) {
this.setDefaultDate(value);
return this.setPageData({ cmCycle: value });
},
},
},
watch: {},
created() {
// this.setDefaultDate(this.searchParam[this.parentPrgmId].cmCycle);
},
async mounted() {},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
...mapActions({}),
setDefaultDate(value) {
// console.log("주기에 따른 오늘 기준 기본 날짜 세팅");
const today = Utility.setFormatDate('today', 'YYYY-MM-DD');
let srartDate = '';
let endDate = '';
// console.log(value);
switch (value) {
case 'CYC_YEAR':
// endDate = today;
srartDate = Utility.setFormatDate(today, 'YYYY');
break;
case 'CYC_MONTH':
// endDate = today;
srartDate = Utility.setFormatDate(today, 'YYYY-MM');
// endDate = today;
// srartDate = Utility.setBeforetDate(
// this.searchParam[this.parentPrgmId],
// endDate,
// "YYYYMMDD"
// );
break;
case 'CYC_DAY':
// endDate = today;
srartDate = today;
break;
default:
break;
}
this.setPageData({ fromDt: srartDate });
// console.log(this.searchParam[this.parentPrgmId].cmCycle);
// console.log(this.searchParam[this.parentPrgmId].dateRange);
},
},
};
</script>
<style></style>

View File

@ -0,0 +1,113 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small color="primary" class="mr-1">mdi-record-circle</v-icon>
{{ label }}
</label>
</v-col>
<v-col :cols="label ? textCols : ''">
<v-radio-group
v-model="selected"
required:rules="radioRules"
row
hide-details
dense
>
<v-radio
label="태그"
value="tag"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
<v-radio
label="검침개소"
value="readPlc"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
<v-radio
label="공정"
value="ecc"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
<v-radio
label="설비"
value="eqpm"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
</v-radio-group>
<!-- @change="updateBlocCode($event)" -->
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
import Utility from '~/plugins/utility';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 7,
},
},
data() {
return {
label: '기준',
labelPrepend: true,
// selected:"CYC_DAY"
};
},
computed: {
...mapState({
isDarkMode: 'isDarkMode',
searchParam: state => state.pageData,
}),
selected: {
get() {
return this.searchParam[this.parentPrgmId].rdbStandard;
},
set(value) {
return this.setPageData({ rdbStandard: value });
},
},
},
watch: {
selected(value) {
// 주기에 따른 오늘 기준 기본 날짜 세팅
this.setDefaultDate(value);
},
},
created() {
// this.setDefaultDate(this.searchParam[this.parentPrgmId].cmCycle);
},
async mounted() {},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
...mapActions({}),
setDefaultDate(value) {
this.setPageData({ rdbStandard: value });
},
},
};
</script>
<style></style>

View File

@ -0,0 +1,99 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
<v-icon x-small color="primary" class="mr-1">mdi-record-circle</v-icon>
{{ label }}
</label>
</v-col>
<v-col :cols="label ? textCols : ''">
<v-radio-group
v-model="selected"
required:rules="radioRules"
row
hide-details
dense
>
<v-radio
label="사용량"
value="use"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
<v-radio
label="비용"
value="cost"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
</v-radio-group>
<!-- @change="updateBlocCode($event)" -->
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
import Utility from '~/plugins/utility';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 7,
},
},
data() {
return {
label: '구분',
labelPrepend: true,
// selected:"CYC_DAY"
};
},
computed: {
...mapState({
isDarkMode: 'isDarkMode',
searchParam: state => state.pageData,
}),
selected: {
get() {
return this.searchParam[this.parentPrgmId].rdbUseCost;
},
set(value) {
return this.setPageData({ rdbUseCost: value });
},
},
},
watch: {
selected(value) {
// 주기에 따른 오늘 기준 기본 날짜 세팅
this.setDefaultDate(value);
},
},
created() {
// this.setDefaultDate(this.searchParam[this.parentPrgmId].cmCycle);
},
async mounted() {},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
...mapActions({}),
setDefaultDate(value) {
this.setPageData({ rdbUseCost: value });
},
},
};
</script>
<style></style>

View File

@ -0,0 +1,97 @@
<template>
<v-row class="search-box" align="center" no-gutters>
<v-col v-if="label" :cols="labelCols">
<label for="" class="search-box-label">
{{ label }}
</label>
</v-col>
<v-col :cols="label ? textCols : ''">
<v-radio-group
v-model="selectedView"
required:rules="radioRules"
row
dense
:hide-details="true"
>
<v-radio
label="전체"
value="viewAll"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
<v-radio
label="차트"
value="viewChart"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
<v-radio
label="그리드"
value="viewGrid"
:ripple="false"
:color="isDarkMode ? '#1b74d7' : '#4777d9'"
on-icon="mdi-record-circle"
></v-radio>
</v-radio-group>
<!-- @change="updateBlocCode($event)" -->
</v-col>
</v-row>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
import Utility from '~/plugins/utility';
export default {
props: {
parentPrgmId: {
type: String,
require: true,
},
labelCols: {
type: Number,
require: false,
default: 4,
},
textCols: {
type: Number,
require: false,
default: 8,
},
},
data() {
return {
label: '',
labelPrepend: true,
// selected:"CYC_DAY"
};
},
computed: {
...mapState({
searchParam: state => state.pageData,
isDarkMode: 'isDarkMode',
}),
selectedView: {
get() {
return this.searchParam[this.parentPrgmId].viewCheck;
},
set(value) {
return this.setPageData({ viewCheck: value });
},
},
},
watch: {},
created() {
// this.setDefaultDate(this.searchParam[this.parentPrgmId].cmCycle);
},
async mounted() {},
methods: {
...mapMutations({ setPageData: 'setPageData' }),
...mapActions({}),
},
};
</script>
<style></style>

View File

@ -0,0 +1,89 @@
<template>
<v-switch
class="theme-switch"
v-model="mode"
@change="themeChange"
></v-switch>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
data() {
return {
mode: null,
};
},
computed: {
...mapState({
isDarkMode: 'isDarkMode',
}),
},
created() {
this.mode = this.isDarkMode;
},
methods: {
...mapMutations({
setThemeChange: 'setThemeChange',
}),
themeChange() {
this.$vuetify.theme.isDark = this.mode;
this.setThemeChange(this.mode);
},
},
};
</script>
<style lang="scss" scoped>
.theme-switch {
display: inline-flex;
width: 47px;
height: 30px;
::v-deep {
.v-input__control,
.v-input__slot {
height: 100%;
}
.v-input--selection-controls__input {
width: 100%;
margin-right: 0;
height: 100%;
}
.v-input--switch__track {
width: 100%;
height: 100%;
border-radius: 50px;
position: initial;
background-color: #f1f0f8;
}
input {
min-height: initial;
}
.v-input--selection-controls__ripple {
display: none;
}
.v-input--switch__thumb {
position: absolute;
width: 26px;
height: 26px;
border-radius: 50%;
background-color: #f2f2f2;
top: 2px;
left: 0;
background-image: url(../../assets/images/icon/ico-theme-light.png);
background-size: 18px 18px;
background-position: center center;
background-repeat: no-repeat;
}
}
&.v-input--is-label-active {
::v-deep {
.v-input--switch__track {
background-color: #383f5d;
}
.v-input--switch__thumb {
transform: translate(38px, 0);
background-image: url(../../assets/images/icon/ico-theme-dark.png);
}
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More