Initial commit
This commit is contained in:
69
app/components/dialog/ApprovalCode.vue
Normal file
69
app/components/dialog/ApprovalCode.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<Caption v-bind="{title: 'Nhập mã duyệt'}"></Caption>
|
||||
<div class="field is-grouped mt-5">
|
||||
<div class="control mr-5" v-for="v in [1,2,3,4]">
|
||||
<input class="input is-dark" style="font-size: 18px; max-width: 40px; font-weight:bold; text-align: center;" type="password"
|
||||
maxlength="1" :id="`input${v}`" v-model="data[v]" @keyup="changeNext(v)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a @click="reset()">Nhập lại</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data: {},
|
||||
code: undefined
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
if(!this.approvalcode && this.$store.state.login.approval_code) this.approvalcode = this.$store.state.login.approval_code
|
||||
if(!this.approvalcode) {
|
||||
let user = await this.$getdata('user', {id: this.$store.state.login.id}, undefined, true)
|
||||
this.approvalcode = user.approval_code
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.reset()
|
||||
},
|
||||
computed: {
|
||||
approvalcode: {
|
||||
get: function() {return this.$store.state['approvalcode']},
|
||||
set: function(val) {this.$store.commit('updateStore', {name: 'approvalcode', data: val})}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkError() {
|
||||
if(Object.keys(this.data).length<4) return true
|
||||
let code = ''
|
||||
for (const [key, value] of Object.entries(this.data)) {
|
||||
if(!this.$empty(value)) code += value.toString()
|
||||
}
|
||||
if(this.$empty(code) || !this.$isNumber(code)) return true
|
||||
this.code = code
|
||||
return false
|
||||
},
|
||||
checkValid() {
|
||||
if(this.checkError()) return this.$snackbar('Mã phê duyệt không hợp lệ')
|
||||
if(this.code!==this.approvalcode) return this.$snackbar('Mã phê duyệt không chính xác')
|
||||
this.$emit('modalevent', {name: 'approvalcode', data: this.code})
|
||||
this.$emit('close')
|
||||
},
|
||||
changeNext(v) {
|
||||
if(this.$empty(this.data[v])) return
|
||||
else if(v===4) return this.checkValid()
|
||||
let doc = document.getElementById(`input${v+1}`)
|
||||
if(doc) doc.focus()
|
||||
},
|
||||
reset() {
|
||||
this.data = {}
|
||||
let doc = document.getElementById(`input1`)
|
||||
if(doc) doc.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
29
app/components/dialog/Confirm.vue
Normal file
29
app/components/dialog/Confirm.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
<p v-html="content"></p>
|
||||
<p class="border-bottom mt-3 mb-5"></p>
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded">
|
||||
<button class="button is-primary has-text-white" @click="confirm()">Đồng ý</button>
|
||||
<button class="button is-dark ml-5" @click="cancel()">Hủy bỏ</button>
|
||||
</div>
|
||||
<div class="control" v-if="duration">
|
||||
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['content', 'duration'],
|
||||
methods: {
|
||||
cancel() {
|
||||
this.$emit('close')
|
||||
},
|
||||
confirm() {
|
||||
this.$emit('modalevent', {name: 'confirm'})
|
||||
this.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
75
app/components/dialog/CountDown.vue
Normal file
75
app/components/dialog/CountDown.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div id="countdown">
|
||||
<div id="countdown-number"></div>
|
||||
<svg><circle r="18" cx="20" cy="20" color="red"></circle></svg>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['duration'],
|
||||
data() {
|
||||
return {
|
||||
timer: undefined,
|
||||
countdown: this.duration || 10
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
var countdownNumberEl = document.getElementById('countdown-number')
|
||||
countdownNumberEl.textContent = this.countdown;
|
||||
this.timer = setInterval(()=>this.startCount(), 1000)
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.timer)
|
||||
},
|
||||
methods: {
|
||||
startCount() {
|
||||
this.countdown -= 1
|
||||
var countdownNumberEl = document.getElementById('countdown-number')
|
||||
countdownNumberEl.textContent = this.countdown;
|
||||
if(this.countdown===0) {
|
||||
clearInterval(this.timer)
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
#countdown {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
#countdown-number {
|
||||
color: black;
|
||||
display: inline-block;
|
||||
line-height: 40px;
|
||||
}
|
||||
:deep(svg) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
transform: rotateY(-180deg) rotateZ(-90deg);
|
||||
}
|
||||
:deep(svg circle) {
|
||||
stroke-dasharray: 113px;
|
||||
stroke-dashoffset: 0px;
|
||||
stroke-linecap: round;
|
||||
stroke-width: 2px;
|
||||
stroke: black;
|
||||
fill: none;
|
||||
animation: countdown 10s linear infinite forwards;
|
||||
}
|
||||
@keyframes countdown {
|
||||
from {
|
||||
stroke-dashoffset: 0px;
|
||||
}
|
||||
to {
|
||||
stroke-dashoffset: 113px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
47
app/components/dialog/Delete.vue
Normal file
47
app/components/dialog/Delete.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div>
|
||||
<p v-html="content"></p>
|
||||
<p class="border-bottom mt-4 mb-5"></p>
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded">
|
||||
<button class="button is-danger" @click="remove()">Đồng ý</button>
|
||||
<button class="button is-dark ml-5" @click="cancel()">Hủy bỏ</button>
|
||||
</div>
|
||||
<div class="control" v-if="duration">
|
||||
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['content', 'duration', 'vbind', 'setdeleted'],
|
||||
methods: {
|
||||
cancel() {
|
||||
this.$emit('close')
|
||||
},
|
||||
async remove() {
|
||||
let pagename = this.vbind.pagename
|
||||
let pagedata = this.$store.state[pagename]
|
||||
let name = pagedata.origin_api.name || this.vbind.api
|
||||
let id = this.vbind.row.id
|
||||
let result
|
||||
if(this.setdeleted) {
|
||||
let record = await this.$getdata(name, {id: id}, undefined, true)
|
||||
record.deleted = 1
|
||||
result = await this.$updateapi(name, record)
|
||||
} else result = await this.$deleteapi(name, id)
|
||||
if(result==='error') return this.$dialog('Đã xảy ra lỗi, xóa dữ liệu thất bại', 'Lỗi', 'Error')
|
||||
this.$snackbar('Dữ liệu đã được xoá khỏi hệ thống', undefined, 'Success')
|
||||
let arr = Array.isArray(id)? id : [{id: id}]
|
||||
let copy = this.$copy(this.$store.state[pagename].data)
|
||||
arr.map(x=>{
|
||||
let index = copy.findIndex(v=>v.id===x.id)
|
||||
index>=0? this.$delete(copy,index) : false
|
||||
})
|
||||
this.$store.commit('updateState', {name: pagename, key: 'update', data: {data: copy}})
|
||||
this.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
31
app/components/dialog/Error.vue
Normal file
31
app/components/dialog/Error.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded pr-3" v-html="content"></div>
|
||||
<div class="control">
|
||||
<SvgIcon v-bind="{name: 'error.svg', type: 'danger', size: 24}"></SvgIcon>
|
||||
</div>
|
||||
</div>
|
||||
<p class="border-bottom mt-3 mb-5"></p>
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded">
|
||||
<button class="button is-danger has-text-white" @click="cancel()">Đóng</button>
|
||||
</div>
|
||||
<div class="control" v-if="duration">
|
||||
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
import { useStore } from '@/stores/index'
|
||||
const store = useStore()
|
||||
var props = defineProps({
|
||||
content: String,
|
||||
duration: Number
|
||||
})
|
||||
function cancel() {
|
||||
this.$emit('close')
|
||||
}
|
||||
</script>
|
||||
24
app/components/dialog/Info.vue
Normal file
24
app/components/dialog/Info.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<p v-html="content"></p>
|
||||
<p class="border-bottom mt-3 mb-5"></p>
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded">
|
||||
<button class="button is-dark" @click="cancel()">Đóng</button>
|
||||
</div>
|
||||
<div class="control" v-if="duration">
|
||||
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['content', 'duration'],
|
||||
methods: {
|
||||
cancel() {
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
110
app/components/dialog/NoteInput.vue
Normal file
110
app/components/dialog/NoteInput.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head" :class="headerClass">
|
||||
<p class="modal-card-title has-text-white">
|
||||
<span class="icon-text">
|
||||
<span class="icon">
|
||||
<SvgIcon :name="iconName" type="white" :size="24" />
|
||||
</span>
|
||||
<span>{{ title }}</span>
|
||||
</span>
|
||||
</p>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="field">
|
||||
<label class="label">{{ label }}</label>
|
||||
<div class="control">
|
||||
<textarea class="textarea" v-model="note" :placeholder="placeholder"></textarea>
|
||||
</div>
|
||||
<p v-if="error" class="help is-danger">{{ error }}</p>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot is-justify-content-flex-end">
|
||||
<button class="button" @click="$emit('close')">{{ cancelText }}</button>
|
||||
<button class="button" :class="buttonClass" @click="confirm">{{ confirmText }}</button>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useStore } from "@/stores/index";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Nhập nội dung'
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: 'Ghi chú'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'Nhập nội dung...'
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'primary', // primary, warning, danger
|
||||
},
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: 'Xác nhận'
|
||||
},
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: 'Hủy'
|
||||
}
|
||||
},
|
||||
emits: ['close', 'modalevent'],
|
||||
setup() {
|
||||
const store = useStore();
|
||||
return { store };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
note: '',
|
||||
error: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isVietnamese() {
|
||||
return this.store.lang === 'vi';
|
||||
},
|
||||
headerClass() {
|
||||
const colorMap = {
|
||||
primary: 'has-background-primary',
|
||||
warning: 'has-background-warning',
|
||||
danger: 'has-background-danger',
|
||||
};
|
||||
return colorMap[this.type] || 'has-background-primary';
|
||||
},
|
||||
buttonClass() {
|
||||
const colorMap = {
|
||||
primary: 'is-primary',
|
||||
warning: 'is-warning',
|
||||
danger: 'is-danger',
|
||||
};
|
||||
return colorMap[this.type] || 'is-primary';
|
||||
},
|
||||
iconName() {
|
||||
const iconMap = {
|
||||
primary: 'edit.svg',
|
||||
warning: 'warning.svg',
|
||||
danger: 'alert.svg',
|
||||
};
|
||||
return iconMap[this.type] || 'edit.svg';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
if (!this.note.trim()) {
|
||||
this.error = this.isVietnamese ? 'Nội dung không được để trống.' : 'Content cannot be empty.';
|
||||
return;
|
||||
}
|
||||
this.$emit('modalevent', { name: 'noteConfirm', data: { note: this.note } });
|
||||
this.$emit('close');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
29
app/components/dialog/Success.vue
Normal file
29
app/components/dialog/Success.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded pr-3" v-html="content"></div>
|
||||
<div class="control">
|
||||
<SvgIcon v-bind="{name: 'check2.svg', type: 'primary', size: 24}"></SvgIcon>
|
||||
</div>
|
||||
</div>
|
||||
<p class="border-bottom mt-3 mb-5"></p>
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded">
|
||||
<button class="button is-primary" @click="cancel()">Đóng</button>
|
||||
</div>
|
||||
<div class="control" v-if="duration">
|
||||
<CountDown v-bind="{duration: duration}" @close="cancel()"></CountDown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ['content', 'duration'],
|
||||
methods: {
|
||||
cancel() {
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user